From b826db374f15d03850dd31a516c0814063e23ac0 Mon Sep 17 00:00:00 2001 From: clearloop Date: Thu, 27 Feb 2020 11:24:14 +0800 Subject: [PATCH 01/19] add: READMEs for balances --- frame/balances/README.adoc | 9 +++++++++ frame/balances/kton/README.adoc | 31 +++++++++++++++++++++++++++++++ frame/balances/ring/README.adoc | 3 +++ 3 files changed, 43 insertions(+) create mode 100644 frame/balances/README.adoc create mode 100644 frame/balances/kton/README.adoc create mode 100644 frame/balances/ring/README.adoc diff --git a/frame/balances/README.adoc b/frame/balances/README.adoc new file mode 100644 index 000000000..35c31fc46 --- /dev/null +++ b/frame/balances/README.adoc @@ -0,0 +1,9 @@ +# Balances + +Balances currently contains **RING** and **KTON**. + +**RING** is system token of Darwinia Network, the initial supply before Darwinia network mainnet release is 2 billion. + +**KTON** is the staking and governance credential of Darwinia Network, KTON can **only obtained by locking RING**, the initial supply is 0. + +At present, some **RING** and **KTON** exist in the Ethereum network and the Tron network in the form of **ERC-20** and **TRC-20**. These TOKENs will be transferred to the Darwinia main network by 1:1 cross-chain conversion after the Darwinia main online. diff --git a/frame/balances/kton/README.adoc b/frame/balances/kton/README.adoc new file mode 100644 index 000000000..31ece1e3f --- /dev/null +++ b/frame/balances/kton/README.adoc @@ -0,0 +1,31 @@ +# Kton + +To encourage users to make long term commitments and pledge, users can choose to lock RING for 3 - 36 months in the process of Staking, and the system will offer a KTON token as reward for users participating in Staking. During the committed pledge period, users can not unlock their RING. (Unless pay triple amounts of KTON as penalty) + +As a result, during RING staking process, user can choose to **lock RING for a period to receive KTON**. The initial supply amount of KTON should be zero, yet before Darwinia Mainnet launch, some users have already started locking their RING in Evolution Land, so there will be some KTON supply at the time of mainnet launch. The earliest design to obtain the KTON by locking the RING appears in the Gringotts of Evolution Land. The related introduction can refer to the Gringotts KTON model [5]. + +KTON can be pledged to receive Staking power, so as to participate POS mining as well. User may Staking via pledge KTON, if user take back their staking KTON, then related POS mining is stopped, and it takes 14 days for unpledged KTON to arrive. + +## FAQ + +### What is Kton? + +**KTON** is the staking and governance credential of Darwinia Network, KTON can **only obtained by locking RING**, the initial supply is 0. + +### Where Kton from? + +Users can choose to lock RING for 3 - 36 months in the process of Staking, and the system will offer a KTON token as reward for users participating in Staking. + +### When Kton spends? + +1. Transfer to other accounts. + +2. Burned by slashed. + +### What will happen after slashing? + +Slashed token goes to tresure. + + +--- +(5): https://forum.evolution.land/topics/55 diff --git a/frame/balances/ring/README.adoc b/frame/balances/ring/README.adoc new file mode 100644 index 000000000..1554d30aa --- /dev/null +++ b/frame/balances/ring/README.adoc @@ -0,0 +1,3 @@ +# RING + +**RING** is system token of Darwinia Network, the initial supply before Darwinia network mainnet release is 2 billion. From c148de4513dee89310faa3f7ec8b4c1d4e443f16 Mon Sep 17 00:00:00 2001 From: clearloop Date: Fri, 28 Feb 2020 16:48:26 +0800 Subject: [PATCH 02/19] add: basic StakingBalance in treasury --- frame/treasury/src/lib.rs | 116 ++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 23 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 6c16baa60..b37c9df5a 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -63,7 +63,7 @@ mod types { pub type RingBalance = as Currency>>::Balance; pub type RingPositiveImbalance = as Currency>>::PositiveImbalance; pub type RingNegativeImbalance = as Currency>>::NegativeImbalance; - + pub type StakingBalanceT = StakingBalance, KtonBalance>; pub type KtonBalance = as Currency>>::Balance; pub type KtonNegativeImbalance = as Currency>>::NegativeImbalance; @@ -83,7 +83,7 @@ use frame_system::{self as system, ensure_signed}; use serde::{Deserialize, Serialize}; use sp_runtime::{ traits::{AccountIdConversion, EnsureOrigin, Saturating, StaticLookup, Zero}, - ModuleId, Permill, + ModuleId, Permill, RuntimeDebug, }; use sp_std::prelude::*; @@ -95,6 +95,7 @@ const MODULE_ID: ModuleId = ModuleId(*b"py/trsry"); pub trait Trait: frame_system::Trait { /// The staking *RING*. type RingCurrency: Currency + ReservableCurrency; + /// The staking *Kton*. type KtonCurrency: Currency + ReservableCurrency; @@ -115,7 +116,7 @@ pub trait Trait: frame_system::Trait { type ProposalBond: Get; /// Minimum amount of funds that should be placed in a deposit for making a proposal. - type ProposalBondMinimum: Get>; + type ProposalBondMinimum: Get>; /// Period between successive spends. type SpendPeriod: Get; @@ -126,6 +127,15 @@ pub trait Trait: frame_system::Trait { type ProposalIndex = u32; +/// To unify *Ring* and *Kton* balances. Ref to the solution at +/// [`darwinia_staking`](../darwinia_staking/enum.StakingBalance.html), +/// keep the `StakingBalance` name for upgrade usages. +#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Ord, PartialOrd)] +pub enum StakingBalance { + RingBalance(RingBalance), + KtonBalance(KtonBalance), +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Fraction of a proposal's value that should be bonded in order to place the proposal. @@ -133,7 +143,7 @@ decl_module! { const ProposalBond: Permill = T::ProposalBond::get(); /// Minimum amount of funds that should be placed in a deposit for making a proposal. - const ProposalBondMinimum: RingBalance = T::ProposalBondMinimum::get(); + const ProposalBondMinimum: StakingBalanceT = T::ProposalBondMinimum::get(); /// Period between successive spends. const SpendPeriod: T::BlockNumber = T::SpendPeriod::get(); @@ -157,21 +167,52 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(500_000)] fn propose_spend( origin, - #[compact] value: RingBalance, + value: StakingBalanceT, beneficiary: ::Source ) { - let proposer = ensure_signed(origin)?; - let beneficiary = T::Lookup::lookup(beneficiary)?; - - let bond = Self::calculate_bond(value); - T::RingCurrency::reserve(&proposer, bond) - .map_err(|_| >::InsufficientProposersBalance)?; - - let c = Self::proposal_count(); - ProposalCount::put(c + 1); - >::insert(c, Proposal { proposer, value, beneficiary, bond }); - - Self::deposit_event(RawEvent::Proposed(c)); + match value { + StakingBalance::RingBalance(value) => { + let proposer = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + // TODO: must be true, error handling. + if let StakingBalance::RingBalance(bond) = Self::calculate_bond( + StakingBalance::RingBalance(value) + ) { + T::RingCurrency::reserve(&proposer, bond) + .map_err(|_| >::InsufficientProposersBalance)?; + + let c = Self::proposal_count(); + ProposalCount::put(c + 1); + >::insert(c, Proposal { proposer, value, beneficiary, bond }); + + Self::deposit_event(RawEvent::Proposed(c)); + } + }, + StakingBalance::KtonBalance(value) => { + // unimplemented!(); + let proposer = ensure_signed(origin)?; + let _beneficiary = T::Lookup::lookup(beneficiary)?; + + // TODO: must be true, error handling. + if let StakingBalance::KtonBalance(bond) = Self::calculate_bond( + StakingBalance::KtonBalance(value) + ) { + T::KtonCurrency::reserve(&proposer, bond) + .map_err(|_| >::InsufficientProposersBalance)?; + + let c = Self::proposal_count(); + ProposalCount::put(c + 1); + + // TODO: @clearloop + // + // This line requires completing StakingBalance store + // >::insert(c, Proposal { proposer, value, beneficiary, bond }); + + Self::deposit_event(RawEvent::Proposed(c)); + } + } + } } /// Reject a proposed spend. The original deposit will be slashed. @@ -189,8 +230,22 @@ decl_module! { let value = proposal.bond; let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; T::ProposalRejection::on_unbalanced(imbalance); - Self::deposit_event(Event::::Rejected(proposal_id, value)); + + + // match value { + // StakingBalance::KtonBalance(value) => { + // let imbalance = T::KtonCurrency::slash_reserved(&proposal.proposer, value).0; + // T::ProposalRejection::on_unbalanced(imbalance); + // Self::deposit_event(Event::::Rejected(proposal_id, StakingBalance::KtonBalance(value))); + // }, + // StakingBalance::RingBalance(value) => { + // let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; + // T::ProposalRejection::on_unbalanced(imbalance); + // Self::deposit_event(Event::::Rejected(proposal_id, StakingBalance::RingBalance(value))); + // } + // } + } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -222,11 +277,11 @@ decl_module! { /// A spending proposal. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, PartialEq, Eq, sp_runtime::RuntimeDebug)] -pub struct Proposal { +pub struct Proposal { proposer: AccountId, - value: RingBalance, + value: StakingBalance, beneficiary: AccountId, - bond: RingBalance, + bond: StakingBalance, } decl_storage! { @@ -247,11 +302,19 @@ decl_storage! { &>::account_id(), T::RingCurrency::minimum_balance(), ); + + // TODO: how to init both Ring and Kton in genesis? + // + // let _ = T::KtonCurrency::make_free_balance_be( + // &>::account_id(), + // T::KtonCurrency::minimum_balance(), + // ); }); } } decl_event!( + /// TODO: Events below needs to replace RingBalance to StakingBalance pub enum Event where ::AccountId, @@ -299,8 +362,15 @@ impl Module { } /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: RingBalance) -> RingBalance { - T::ProposalBondMinimum::get().max(T::ProposalBond::get() * value) + fn calculate_bond(value: StakingBalanceT) -> StakingBalanceT { + match value { + StakingBalance::KtonBalance(value) => { + T::ProposalBondMinimum::get().max(StakingBalance::KtonBalance(T::ProposalBond::get() * value)) + } + StakingBalance::RingBalance(value) => { + T::ProposalBondMinimum::get().max(StakingBalance::RingBalance(T::ProposalBond::get() * value)) + } + } } // Spend some money! From 829657502ab769656a38bb8b8003703626ca587b Mon Sep 17 00:00:00 2001 From: clearloop Date: Fri, 28 Feb 2020 20:16:00 +0800 Subject: [PATCH 03/19] update: convert Ring to StakingBalance in treasury --- frame/treasury/src/lib.rs | 174 ++++++++++++++++++-------------------- 1 file changed, 82 insertions(+), 92 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index b37c9df5a..137e68bac 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -109,7 +109,8 @@ pub trait Trait: frame_system::Trait { type Event: From> + Into<::Event>; /// Handler for the unbalanced decrease when slashing for a rejected proposal. - type ProposalRejection: OnUnbalanced>; + type RingProposalRejection: OnUnbalanced>; + type KtonProposalRejection: OnUnbalanced>; /// Fraction of a proposal's value that should be bonded in order to place the proposal. /// An accepted proposal gets these back. A rejected proposal does not. @@ -130,7 +131,7 @@ type ProposalIndex = u32; /// To unify *Ring* and *Kton* balances. Ref to the solution at /// [`darwinia_staking`](../darwinia_staking/enum.StakingBalance.html), /// keep the `StakingBalance` name for upgrade usages. -#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, Ord, PartialOrd)] +#[derive(Copy, Clone, Encode, Decode, Eq, Ord, RuntimeDebug, PartialEq, PartialOrd)] pub enum StakingBalance { RingBalance(RingBalance), KtonBalance(KtonBalance), @@ -170,49 +171,30 @@ decl_module! { value: StakingBalanceT, beneficiary: ::Source ) { - match value { - StakingBalance::RingBalance(value) => { - let proposer = ensure_signed(origin)?; - let beneficiary = T::Lookup::lookup(beneficiary)?; - - // TODO: must be true, error handling. - if let StakingBalance::RingBalance(bond) = Self::calculate_bond( - StakingBalance::RingBalance(value) - ) { - T::RingCurrency::reserve(&proposer, bond) - .map_err(|_| >::InsufficientProposersBalance)?; - - let c = Self::proposal_count(); - ProposalCount::put(c + 1); - >::insert(c, Proposal { proposer, value, beneficiary, bond }); - - Self::deposit_event(RawEvent::Proposed(c)); - } + let proposer = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + let bond = Self::calculate_bond(value); + match bond { + StakingBalance::KtonBalance(bond) => { + T::KtonCurrency::reserve(&proposer, bond) + .map_err(|_| >::InsufficientProposersBalance)?; }, - StakingBalance::KtonBalance(value) => { - // unimplemented!(); - let proposer = ensure_signed(origin)?; - let _beneficiary = T::Lookup::lookup(beneficiary)?; - - // TODO: must be true, error handling. - if let StakingBalance::KtonBalance(bond) = Self::calculate_bond( - StakingBalance::KtonBalance(value) - ) { - T::KtonCurrency::reserve(&proposer, bond) - .map_err(|_| >::InsufficientProposersBalance)?; - - let c = Self::proposal_count(); - ProposalCount::put(c + 1); - - // TODO: @clearloop - // - // This line requires completing StakingBalance store - // >::insert(c, Proposal { proposer, value, beneficiary, bond }); - - Self::deposit_event(RawEvent::Proposed(c)); - } + StakingBalance::RingBalance(bond) => { + T::RingCurrency::reserve(&proposer, bond) + .map_err(|_| >::InsufficientProposersBalance)?; } } + + let c = Self::proposal_count(); + ProposalCount::put(c + 1); + >::insert(c, Proposal { + proposer, + beneficiary, + bond, + value, + }); + + Self::deposit_event(RawEvent::Proposed(c)); } /// Reject a proposed spend. The original deposit will be slashed. @@ -228,24 +210,18 @@ decl_module! { let proposal = >::take(&proposal_id).ok_or(>::InvalidProposalIndex)?; let value = proposal.bond; - let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; - T::ProposalRejection::on_unbalanced(imbalance); - Self::deposit_event(Event::::Rejected(proposal_id, value)); - - - // match value { - // StakingBalance::KtonBalance(value) => { - // let imbalance = T::KtonCurrency::slash_reserved(&proposal.proposer, value).0; - // T::ProposalRejection::on_unbalanced(imbalance); - // Self::deposit_event(Event::::Rejected(proposal_id, StakingBalance::KtonBalance(value))); - // }, - // StakingBalance::RingBalance(value) => { - // let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; - // T::ProposalRejection::on_unbalanced(imbalance); - // Self::deposit_event(Event::::Rejected(proposal_id, StakingBalance::RingBalance(value))); - // } - // } + match value { + StakingBalance::KtonBalance(value) => { + let imbalance = T::KtonCurrency::slash_reserved(&proposal.proposer, value).0; + T::KtonProposalRejection::on_unbalanced(imbalance); + }, + StakingBalance::RingBalance(value) => { + let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; + T::RingProposalRejection::on_unbalanced(imbalance); + } + } + Self::deposit_event(Event::::Rejected(proposal_id, value)); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -268,7 +244,8 @@ decl_module! { fn on_finalize(n: T::BlockNumber) { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { - Self::spend_funds(); + Self::spend_funds::(); + Self::spend_funds::(); } } } @@ -290,7 +267,7 @@ decl_storage! { ProposalCount get(fn proposal_count): ProposalIndex; /// Proposals that have been made. - Proposals get(fn proposals): map ProposalIndex => Option>>; + Proposals get(fn proposals): map ProposalIndex => Option>>; /// Proposal indices that have been approved but not yet awarded. Approvals get(fn approvals): Vec; @@ -303,12 +280,10 @@ decl_storage! { T::RingCurrency::minimum_balance(), ); - // TODO: how to init both Ring and Kton in genesis? - // - // let _ = T::KtonCurrency::make_free_balance_be( - // &>::account_id(), - // T::KtonCurrency::minimum_balance(), - // ); + let _ = T::KtonCurrency::make_free_balance_be( + &>::account_id(), + T::KtonCurrency::minimum_balance(), + ); }); } } @@ -324,15 +299,15 @@ decl_event!( /// New proposal. Proposed(ProposalIndex), /// We have ended a spend period and will now allocate funds. - Spending(RingBalance), + Spending(StakingBalance), /// Some funds have been allocated. - Awarded(ProposalIndex, RingBalance, AccountId), + Awarded(ProposalIndex, StakingBalance, AccountId), /// A proposal was rejected; funds were slashed. - Rejected(ProposalIndex, RingBalance), + Rejected(ProposalIndex, StakingBalance), /// Some of our funds have been burnt. - Burnt(RingBalance), + Burnt(StakingBalance), /// Spending has finished; this is the amount that rolls over until next spend. - Rollover(RingBalance), + Rollover(StakingBalance), /// Some *Ring* have been deposited. DepositRing(RingBalance), /// Some *Kton* have been deposited. @@ -374,34 +349,45 @@ impl Module { } // Spend some money! - fn spend_funds() { - let mut budget_remaining = Self::pot(); - Self::deposit_event(RawEvent::Spending(budget_remaining)); + fn spend_funds>() { + let mut budget_remaining = Self::pot::(); + Self::deposit_event(RawEvent::Spending(StakingBalance::RingBalance(budget_remaining))); let mut missed_any = false; let mut imbalance = >::zero(); Approvals::mutate(|v| { v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. - if let Some(p) = Self::proposals(index) { - if p.value <= budget_remaining { - budget_remaining -= p.value; + let option_proposal = Self::proposals(index); + if option_proposal.is_none() { + return false; + } + + let p = option_proposal.unwrap(); + match (p.value, p.bond) { + (StakingBalance::RingBalance(value), StakingBalance::RingBalance(bond)) => { + if value > budget_remaining { + missed_any = true; + return true; + } + + budget_remaining -= value; >::remove(index); // return their deposit. - let _ = T::RingCurrency::unreserve(&p.proposer, p.bond); + let _ = T::RingCurrency::unreserve(&p.proposer, bond); // provide the allocation. - imbalance.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.value)); + imbalance.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, value)); - Self::deposit_event(RawEvent::Awarded(index, p.value, p.beneficiary)); + Self::deposit_event(RawEvent::Awarded( + index, + StakingBalance::RingBalance(value), + p.beneficiary, + )); false - } else { - missed_any = true; - true } - } else { - false + _ => false, } }); }); @@ -411,7 +397,7 @@ impl Module { let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); budget_remaining -= burn; imbalance.subsume(T::RingCurrency::burn(burn)); - Self::deposit_event(RawEvent::Burnt(burn)) + Self::deposit_event(RawEvent::Burnt(StakingBalance::RingBalance(burn))) } // Must never be an error, but better to be safe. @@ -429,15 +415,18 @@ impl Module { drop(problem); } - Self::deposit_event(RawEvent::Rollover(budget_remaining)); + Self::deposit_event(RawEvent::Rollover(StakingBalance::RingBalance(budget_remaining))); } /// Return the amount of money in the pot. // The existential deposit is not part of the pot so treasury account never gets deleted. - fn pot() -> RingBalance { - T::RingCurrency::free_balance(&Self::account_id()) + fn pot() -> C::Balance + where + C: Currency, + { + C::free_balance(&Self::account_id()) // Must never be less than 0 but better be safe. - .saturating_sub(T::RingCurrency::minimum_balance()) + .saturating_sub(C::minimum_balance()) } } @@ -546,7 +535,8 @@ mod tests { type ApproveOrigin = frame_system::EnsureRoot; type RejectOrigin = frame_system::EnsureRoot; type Event = (); - type ProposalRejection = (); + type KtonProposalRejection = (); + type RingProposalRejection = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; type SpendPeriod = SpendPeriod; From 13d04c91daff0fbeb405137e3dd145e6e74aa880 Mon Sep 17 00:00:00 2001 From: clearloop Date: Fri, 28 Feb 2020 21:24:49 +0800 Subject: [PATCH 04/19] fix: prev treasury tests --- frame/treasury/src/lib.rs | 143 +++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 41 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 137e68bac..a5098027f 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -137,6 +137,16 @@ pub enum StakingBalance { KtonBalance(KtonBalance), } +impl Default for StakingBalance +where + RingBalance: Default, + KtonBalance: Default, +{ + fn default() -> Self { + StakingBalance::RingBalance(Default::default()) + } +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Fraction of a proposal's value that should be bonded in order to place the proposal. @@ -245,7 +255,7 @@ decl_module! { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { Self::spend_funds::(); - Self::spend_funds::(); + // Self::spend_funds::(); } } } @@ -456,7 +466,6 @@ impl OnUnbalancedKton> for Module { #[cfg(test)] mod tests { use crate::*; - use frame_support::{assert_noop, assert_ok, impl_outer_origin, parameter_types, weights::Weight}; use sp_core::H256; use sp_runtime::{ @@ -525,7 +534,7 @@ mod tests { } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); - pub const ProposalBondMinimum: u64 = 1; + // pub const ProposalBondMinimum: u64 = 1; pub const SpendPeriod: u64 = 2; pub const Burn: Permill = Permill::from_percent(50); } @@ -538,7 +547,7 @@ mod tests { type KtonProposalRejection = (); type RingProposalRejection = (); type ProposalBond = ProposalBond; - type ProposalBondMinimum = ProposalBondMinimum; + type ProposalBondMinimum = (); type SpendPeriod = SpendPeriod; type Burn = Burn; } @@ -564,7 +573,7 @@ mod tests { #[test] fn genesis_config_works() { new_test_ext().execute_with(|| { - assert_eq!(Treasury::pot(), 0); + assert_eq!(Treasury::pot::(), 0); assert_eq!(Treasury::proposal_count(), 0); }); } @@ -574,23 +583,32 @@ mod tests { new_test_ext().execute_with(|| { // Check that accumulate works when we have Some value in Dummy already. Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); + assert_eq!(Treasury::pot::(), 100); }); } + /// min deposit is 0 now #[test] fn spend_proposal_takes_min_deposit() { new_test_ext().execute_with(|| { - assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); - assert_eq!(Ring::free_balance(&0), 99); - assert_eq!(Ring::reserved_balance(&0), 1); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(1), + 3 + )); + assert_eq!(Ring::free_balance(&0), 100); + assert_eq!(Ring::reserved_balance(&0), 0); }); } #[test] fn spend_proposal_takes_proportional_deposit() { new_test_ext().execute_with(|| { - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_eq!(Ring::free_balance(&0), 95); assert_eq!(Ring::reserved_balance(&0), 5); }); @@ -600,7 +618,7 @@ mod tests { fn spend_proposal_fails_when_proposer_poor() { new_test_ext().execute_with(|| { assert_noop!( - Treasury::propose_spend(Origin::signed(2), 100, 3), + Treasury::propose_spend(Origin::signed(2), StakingBalance::RingBalance(100), 3), Error::::InsufficientProposersBalance, ); }); @@ -611,12 +629,16 @@ mod tests { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(1); assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot(), 100); + assert_eq!(Treasury::pot::(), 100); }); } @@ -628,7 +650,7 @@ mod tests { assert_eq!(Ring::total_issuance(), init_total_issuance + 100); >::on_finalize(2); - assert_eq!(Treasury::pot(), 50); + assert_eq!(Treasury::pot::(), 50); assert_eq!(Ring::total_issuance(), init_total_issuance + 50); }); } @@ -638,12 +660,16 @@ mod tests { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); >::on_finalize(2); assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot(), 50); + assert_eq!(Treasury::pot::(), 50); }); } @@ -652,7 +678,11 @@ mod tests { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); assert_noop!( Treasury::reject_proposal(Origin::ROOT, 0), @@ -686,7 +716,11 @@ mod tests { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); assert_noop!( Treasury::approve_proposal(Origin::ROOT, 0), @@ -699,14 +733,18 @@ mod tests { fn accepted_spend_proposal_enacted_on_spend_period() { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); + assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); assert_eq!(Ring::free_balance(&3), 100); - assert_eq!(Treasury::pot(), 0); + assert_eq!(Treasury::pot::(), 0); }); } @@ -714,18 +752,21 @@ mod tests { fn pot_underflow_should_not_diminish() { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 3)); + assert_eq!(Treasury::pot::(), 100); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(150), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); - assert_eq!(Treasury::pot(), 100); // Pot hasn't changed + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed let _ = Ring::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); >::on_finalize(4); assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent - assert_eq!(Treasury::pot(), 25); // Pot has finally changed + assert_eq!(Treasury::pot::(), 25); // Pot has finally changed }); } @@ -735,20 +776,24 @@ mod tests { fn treasury_account_doesnt_get_deleted() { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot(), 100); - let treasury_balance = Ring::free_balance(&Treasury::account_id()); + assert_eq!(Treasury::pot::(), 100); + let ring_treasury_balance = StakingBalance::RingBalance(Ring::free_balance(&Treasury::account_id())); - assert_ok!(Treasury::propose_spend(Origin::signed(0), treasury_balance, 3)); + assert_ok!(Treasury::propose_spend(Origin::signed(0), ring_treasury_balance, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); - assert_eq!(Treasury::pot(), 100); // Pot hasn't changed + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - assert_ok!(Treasury::propose_spend(Origin::signed(0), Treasury::pot(), 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(Treasury::pot::()), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); >::on_finalize(4); - assert_eq!(Treasury::pot(), 0); // Pot is emptied + assert_eq!(Treasury::pot::(), 0); // Pot is emptied assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there }); } @@ -768,24 +813,40 @@ mod tests { let mut t: sp_io::TestExternalities = t.into(); t.execute_with(|| { - assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); // Account does not exist - assert_eq!(Treasury::pot(), 0); // Pot is empty - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 99, 3)); + // Account does not exist + assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); + assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); + + // Pot is empty + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(99), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 3)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(1), + 3 + )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + >::on_finalize(2); - assert_eq!(Treasury::pot(), 0); // Pot hasn't changed - assert_eq!(Ring::free_balance(&3), 0); // Balance of `3` hasn't changed + // Pot hasn't changed + assert_eq!(Treasury::pot::(), 0); + // Balance of `3` hasn't changed + assert_eq!(Ring::free_balance(&3), 0); Ring::make_free_balance_be(&Treasury::account_id(), 100); - assert_eq!(Treasury::pot(), 99); // Pot now contains funds + assert_eq!(Treasury::pot::(), 99); // Pot now contains funds assert_eq!(Ring::free_balance(&Treasury::account_id()), 100); // Account does exist >::on_finalize(4); - assert_eq!(Treasury::pot(), 0); // Pot has changed + assert_eq!(Treasury::pot::(), 0); // Pot has changed assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed }); } From c15d48652c492d20fbf515a806e114608e3316dd Mon Sep 17 00:00:00 2001 From: clearloop Date: Sat, 29 Feb 2020 00:10:21 +0800 Subject: [PATCH 05/19] fix: tests in runtime relating to treasury --- bin/node/runtime/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index efd7376c8..30ce193b3 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -501,9 +501,10 @@ impl pallet_treasury::Trait for Runtime { type ApproveOrigin = pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective>; type RejectOrigin = pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective>; type Event = Event; - type ProposalRejection = (); + type KtonProposalRejection = (); + type RingProposalRejection = (); type ProposalBond = ProposalBond; - type ProposalBondMinimum = ProposalBondMinimum; + type ProposalBondMinimum = (); type SpendPeriod = SpendPeriod; type Burn = Burn; } From 57e3fa34bad94c075bd205e707173820f8c9cd1a Mon Sep 17 00:00:00 2001 From: clearloop Date: Sat, 29 Feb 2020 18:16:18 +0800 Subject: [PATCH 06/19] fix: ProposalBondMinimum in treasury --- .travis.yml | 4 ++++ frame/treasury/src/lib.rs | 33 ++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index bab8431c2..b53cc5472 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,10 @@ jobs: env: RUST_TOOLCHAIN=nightly-2020-02-07 TARGET=native STAKING script: .maintain/ci/darwinia_test_script.sh staking + - stage: Darwinia Test + env: RUST_TOOLCHAIN=nightly-2020-02-07 TARGET=native STAKING + script: .maintain/ci/darwinia_test_script.sh treasury + # TODO: remove this when overall test case ready allow_failures: - stage: Overall Test diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index a5098027f..2cfbb2344 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -63,9 +63,9 @@ mod types { pub type RingBalance = as Currency>>::Balance; pub type RingPositiveImbalance = as Currency>>::PositiveImbalance; pub type RingNegativeImbalance = as Currency>>::NegativeImbalance; - pub type StakingBalanceT = StakingBalance, KtonBalance>; pub type KtonBalance = as Currency>>::Balance; pub type KtonNegativeImbalance = as Currency>>::NegativeImbalance; + pub type StakingBalanceT = StakingBalance, KtonBalance>; type AccountId = ::AccountId; type RingCurrency = ::RingCurrency; @@ -117,7 +117,8 @@ pub trait Trait: frame_system::Trait { type ProposalBond: Get; /// Minimum amount of funds that should be placed in a deposit for making a proposal. - type ProposalBondMinimum: Get>; + type RingProposalBondMinimum: Get>; + type KtonProposalBondMinimum: Get>; /// Period between successive spends. type SpendPeriod: Get; @@ -147,6 +148,16 @@ where } } +impl StakingBalance { + fn ring(v: RingBalance) -> Self { + StakingBalance::RingBalance(v) + } + + fn kton(v: KtonBalance) -> Self { + StakingBalance::KtonBalance(v) + } +} + decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Fraction of a proposal's value that should be bonded in order to place the proposal. @@ -154,7 +165,8 @@ decl_module! { const ProposalBond: Permill = T::ProposalBond::get(); /// Minimum amount of funds that should be placed in a deposit for making a proposal. - const ProposalBondMinimum: StakingBalanceT = T::ProposalBondMinimum::get(); + const KtonProposalBondMinimum: KtonBalance = T::KtonProposalBondMinimum::get(); + const RingProposalBondMinimum: RingBalance = T::RingProposalBondMinimum::get(); /// Period between successive spends. const SpendPeriod: T::BlockNumber = T::SpendPeriod::get(); @@ -350,15 +362,16 @@ impl Module { fn calculate_bond(value: StakingBalanceT) -> StakingBalanceT { match value { StakingBalance::KtonBalance(value) => { - T::ProposalBondMinimum::get().max(StakingBalance::KtonBalance(T::ProposalBond::get() * value)) + StakingBalance::kton(T::KtonProposalBondMinimum::get().max(T::ProposalBond::get() * value)) } StakingBalance::RingBalance(value) => { - T::ProposalBondMinimum::get().max(StakingBalance::RingBalance(T::ProposalBond::get() * value)) + StakingBalance::ring(T::RingProposalBondMinimum::get().max(T::ProposalBond::get() * value)) } } } // Spend some money! + /// TODO: implement spending Kton funds. fn spend_funds>() { let mut budget_remaining = Self::pot::(); Self::deposit_event(RawEvent::Spending(StakingBalance::RingBalance(budget_remaining))); @@ -534,7 +547,8 @@ mod tests { } parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); - // pub const ProposalBondMinimum: u64 = 1; + pub const RingProposalBondMinimum: u64 = 1; + pub const KtonProposalBondMinimum: u64 = 1; pub const SpendPeriod: u64 = 2; pub const Burn: Permill = Permill::from_percent(50); } @@ -547,7 +561,8 @@ mod tests { type KtonProposalRejection = (); type RingProposalRejection = (); type ProposalBond = ProposalBond; - type ProposalBondMinimum = (); + type RingProposalBondMinimum = RingProposalBondMinimum; + type KtonProposalBondMinimum = KtonProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; } @@ -596,8 +611,8 @@ mod tests { StakingBalance::RingBalance(1), 3 )); - assert_eq!(Ring::free_balance(&0), 100); - assert_eq!(Ring::reserved_balance(&0), 0); + assert_eq!(Ring::free_balance(&0), 99); + assert_eq!(Ring::reserved_balance(&0), 1); }); } From d8b7f860842e5e1f7110ac37bd13d6c9a90b869c Mon Sep 17 00:00:00 2001 From: clearloop Date: Sat, 29 Feb 2020 18:16:43 +0800 Subject: [PATCH 07/19] travis: add local test cases for darwinia --- .maintain/ci/travis.local.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .maintain/ci/travis.local.sh diff --git a/.maintain/ci/travis.local.sh b/.maintain/ci/travis.local.sh new file mode 100644 index 000000000..d1c04bace --- /dev/null +++ b/.maintain/ci/travis.local.sh @@ -0,0 +1,15 @@ +readonly TEST_CRATES=( + 'kton' + 'ring' + 'staking' + 'treasury' +); + +function main() { + for crate in ${TEST_CRATES[@]} + do + cargo test -p "darwinia-$crate" + done +} + +main From 2ba401bf1264cd90b4e77e3c505eaf7e339fde32 Mon Sep 17 00:00:00 2001 From: clearloop Date: Sat, 29 Feb 2020 18:30:57 +0800 Subject: [PATCH 08/19] fix: treasury deps in node-runtime patch: cargo build to travis.local.sh --- .maintain/ci/travis.local.sh | 2 ++ bin/node/runtime/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.maintain/ci/travis.local.sh b/.maintain/ci/travis.local.sh index d1c04bace..5ee3911fd 100644 --- a/.maintain/ci/travis.local.sh +++ b/.maintain/ci/travis.local.sh @@ -6,6 +6,8 @@ readonly TEST_CRATES=( ); function main() { + cargo build + for crate in ${TEST_CRATES[@]} do cargo test -p "darwinia-$crate" diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 30ce193b3..fdda83446 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -504,7 +504,8 @@ impl pallet_treasury::Trait for Runtime { type KtonProposalRejection = (); type RingProposalRejection = (); type ProposalBond = ProposalBond; - type ProposalBondMinimum = (); + type RingProposalBondMinimum = (); + type KtonProposalBondMinimum = (); type SpendPeriod = SpendPeriod; type Burn = Burn; } From 888f2332f0b3aa82112944c6e45bde61af195f1f Mon Sep 17 00:00:00 2001 From: clearloop Date: Sun, 1 Mar 2020 18:49:32 +0800 Subject: [PATCH 09/19] add: spend_funds for kton --- .cargo/config | 3 ++ frame/treasury/src/lib.rs | 111 ++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 000000000..bd0db23ab --- /dev/null +++ b/.cargo/config @@ -0,0 +1,3 @@ +[build] +rustc-wrapper = "" +target-dir = "target" \ No newline at end of file diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 2cfbb2344..d74d0d729 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -64,6 +64,7 @@ mod types { pub type RingPositiveImbalance = as Currency>>::PositiveImbalance; pub type RingNegativeImbalance = as Currency>>::NegativeImbalance; pub type KtonBalance = as Currency>>::Balance; + pub type KtonPositiveImbalance = as Currency>>::PositiveImbalance; pub type KtonNegativeImbalance = as Currency>>::NegativeImbalance; pub type StakingBalanceT = StakingBalance, KtonBalance>; @@ -73,6 +74,7 @@ mod types { } use codec::{Decode, Encode}; +use darwinia_support::OnUnbalancedKton; use frame_support::{ decl_error, decl_event, decl_module, decl_storage, ensure, print, traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, ReservableCurrency, WithdrawReason}, @@ -86,8 +88,6 @@ use sp_runtime::{ ModuleId, Permill, RuntimeDebug, }; use sp_std::prelude::*; - -use darwinia_support::OnUnbalancedKton; use types::*; const MODULE_ID: ModuleId = ModuleId(*b"py/trsry"); @@ -129,6 +129,13 @@ pub trait Trait: frame_system::Trait { type ProposalIndex = u32; +/// Reversed for manually usage +#[allow(unused)] +enum Funds { + Kton, + Ring, +} + /// To unify *Ring* and *Kton* balances. Ref to the solution at /// [`darwinia_staking`](../darwinia_staking/enum.StakingBalance.html), /// keep the `StakingBalance` name for upgrade usages. @@ -138,16 +145,6 @@ pub enum StakingBalance { KtonBalance(KtonBalance), } -impl Default for StakingBalance -where - RingBalance: Default, - KtonBalance: Default, -{ - fn default() -> Self { - StakingBalance::RingBalance(Default::default()) - } -} - impl StakingBalance { fn ring(v: RingBalance) -> Self { StakingBalance::RingBalance(v) @@ -263,11 +260,13 @@ decl_module! { Approvals::mutate(|v| v.push(proposal_id)); } + /// This function will implement the `OnFinalize` trait fn on_finalize(n: T::BlockNumber) { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { - Self::spend_funds::(); - // Self::spend_funds::(); + Self::spend_funds(Funds::Ring); + // Self::spend_funds(Funds::Kton); + } } } @@ -370,9 +369,77 @@ impl Module { } } - // Spend some money! - /// TODO: implement spending Kton funds. - fn spend_funds>() { + /// TODO: Need a pre-logic to infer what specific to do with kton or ring. + fn spend_kton_funds() { + let mut budget_remaining = Self::pot::(); + Self::deposit_event(RawEvent::Spending(StakingBalance::kton(budget_remaining))); + + let mut missed_any = false; + let mut imbalance = >::zero(); + Approvals::mutate(|v| { + v.retain(|&index| { + // Should always be true, but shouldn't panic if false or we're screwed. + let option_proposal = Self::proposals(index); + if option_proposal.is_none() { + return false; + } + + let p = option_proposal.unwrap(); + match (p.value, p.bond) { + (StakingBalance::KtonBalance(value), StakingBalance::KtonBalance(bond)) => { + if value > budget_remaining { + missed_any = true; + return true; + } + + budget_remaining -= value; + >::remove(index); + + // return their deposit. + let _ = T::KtonCurrency::unreserve(&p.proposer, bond); + + // provide the allocation. + imbalance.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, value)); + + Self::deposit_event(RawEvent::Awarded( + index, + StakingBalance::KtonBalance(value), + p.beneficiary, + )); + false + } + _ => false, + } + }); + }); + + if !missed_any { + // burn some proportion of the remaining budget if we run a surplus. + let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); + budget_remaining -= burn; + imbalance.subsume(T::KtonCurrency::burn(burn)); + Self::deposit_event(RawEvent::Burnt(StakingBalance::kton(burn))) + } + + // Must never be an error, but better to be safe. + // proof: budget_remaining is account free balance minus ED; + // Thus we can't spend more than account free balance minus ED; + // Thus account is kept alive; qed; + if let Err(problem) = T::KtonCurrency::settle( + &Self::account_id(), + imbalance, + WithdrawReason::Transfer.into(), + ExistenceRequirement::KeepAlive, + ) { + print("Inconsistent state - couldn't settle imbalance for funds spent by treasury"); + // Nothing else to do here. + drop(problem); + } + + Self::deposit_event(RawEvent::Rollover(StakingBalance::kton(budget_remaining))); + } + + fn spend_ring_funds() { let mut budget_remaining = Self::pot::(); Self::deposit_event(RawEvent::Spending(StakingBalance::RingBalance(budget_remaining))); @@ -441,6 +508,14 @@ impl Module { Self::deposit_event(RawEvent::Rollover(StakingBalance::RingBalance(budget_remaining))); } + // Spend some money! + fn spend_funds(fund: Funds) { + match fund { + Funds::Kton => Self::spend_kton_funds(), + Funds::Ring => Self::spend_ring_funds(), + } + } + /// Return the amount of money in the pot. // The existential deposit is not part of the pot so treasury account never gets deleted. fn pot() -> C::Balance @@ -589,6 +664,7 @@ mod tests { fn genesis_config_works() { new_test_ext().execute_with(|| { assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); assert_eq!(Treasury::proposal_count(), 0); }); } @@ -599,6 +675,7 @@ mod tests { // Check that accumulate works when we have Some value in Dummy already. Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); + // assert_eq!(Treasury::pot::(), 0); }); } From fad2d13538592270584519971c8fcdd5a1105551 Mon Sep 17 00:00:00 2001 From: clearloop Date: Sun, 1 Mar 2020 20:10:44 +0800 Subject: [PATCH 10/19] build: unlink .cargo/config --- .cargo/config | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index bd0db23ab..000000000 --- a/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[build] -rustc-wrapper = "" -target-dir = "target" \ No newline at end of file From d6d62db1bc22865d128a291472165a5c69af889f Mon Sep 17 00:00:00 2001 From: clearloop Date: Sun, 1 Mar 2020 23:49:19 +0800 Subject: [PATCH 11/19] add: tests for kton --- .travis.yml | 2 +- frame/balances/kton/src/lib.rs | 2 +- frame/balances/ring/src/lib.rs | 2 +- frame/treasury/src/lib.rs | 238 ++++++++++++++++++++++++++++++--- 4 files changed, 222 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index b53cc5472..aaf3886d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ jobs: script: .maintain/ci/darwinia_test_script.sh staking - stage: Darwinia Test - env: RUST_TOOLCHAIN=nightly-2020-02-07 TARGET=native STAKING + env: RUST_TOOLCHAIN=nightly-2020-02-07 TARGET=native TREASURY script: .maintain/ci/darwinia_test_script.sh treasury # TODO: remove this when overall test case ready diff --git a/frame/balances/kton/src/lib.rs b/frame/balances/kton/src/lib.rs index 3e05c41ee..753f30f18 100644 --- a/frame/balances/kton/src/lib.rs +++ b/frame/balances/kton/src/lib.rs @@ -120,7 +120,7 @@ decl_error! { } decl_storage! { - trait Store for Module as Balances { + trait Store for Module as KtonBalances { /// The total units issued in the system. pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) diff --git a/frame/balances/ring/src/lib.rs b/frame/balances/ring/src/lib.rs index 924957920..af2512685 100644 --- a/frame/balances/ring/src/lib.rs +++ b/frame/balances/ring/src/lib.rs @@ -325,7 +325,7 @@ decl_error! { } decl_storage! { - trait Store for Module, I: Instance=DefaultInstance> as Balances { + trait Store for Module, I: Instance=DefaultInstance> as RingBalances { /// The total units issued in the system. pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { config.balances.iter().fold(Zero::zero(), |acc: T::Balance, &(_, n)| acc + n) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index d74d0d729..388b406de 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -265,8 +265,7 @@ decl_module! { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { Self::spend_funds(Funds::Ring); - // Self::spend_funds(Funds::Kton); - + Self::spend_funds(Funds::Kton); } } } @@ -408,7 +407,7 @@ impl Module { )); false } - _ => false, + _ => true, } }); }); @@ -418,7 +417,7 @@ impl Module { let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); budget_remaining -= burn; imbalance.subsume(T::KtonCurrency::burn(burn)); - Self::deposit_event(RawEvent::Burnt(StakingBalance::kton(burn))) + Self::deposit_event(RawEvent::Burnt(StakingBalance::KtonBalance(burn))) } // Must never be an error, but better to be safe. @@ -436,7 +435,7 @@ impl Module { drop(problem); } - Self::deposit_event(RawEvent::Rollover(StakingBalance::kton(budget_remaining))); + Self::deposit_event(RawEvent::Rollover(StakingBalance::KtonBalance(budget_remaining))); } fn spend_ring_funds() { @@ -477,7 +476,7 @@ impl Module { )); false } - _ => false, + _ => true, } }); }); @@ -675,7 +674,11 @@ mod tests { // Check that accumulate works when we have Some value in Dummy already. Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); - // assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + + // Make sure kton and ring have different storages + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); }); } @@ -683,6 +686,7 @@ mod tests { #[test] fn spend_proposal_takes_min_deposit() { new_test_ext().execute_with(|| { + // ring assert_ok!(Treasury::propose_spend( Origin::signed(0), StakingBalance::RingBalance(1), @@ -690,12 +694,22 @@ mod tests { )); assert_eq!(Ring::free_balance(&0), 99); assert_eq!(Ring::reserved_balance(&0), 1); + + // kton + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(1), + 3 + )); + assert_eq!(Kton::free_balance(&0), 99); + assert_eq!(Kton::reserved_balance(&0), 1); }); } #[test] fn spend_proposal_takes_proportional_deposit() { new_test_ext().execute_with(|| { + // ring assert_ok!(Treasury::propose_spend( Origin::signed(0), StakingBalance::RingBalance(100), @@ -703,24 +717,40 @@ mod tests { )); assert_eq!(Ring::free_balance(&0), 95); assert_eq!(Ring::reserved_balance(&0), 5); + + // kton + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_eq!(Kton::free_balance(&0), 95); + assert_eq!(Kton::reserved_balance(&0), 5); }); } #[test] fn spend_proposal_fails_when_proposer_poor() { new_test_ext().execute_with(|| { + // ring assert_noop!( Treasury::propose_spend(Origin::signed(2), StakingBalance::RingBalance(100), 3), Error::::InsufficientProposersBalance, ); + + // kton + assert_noop!( + Treasury::propose_spend(Origin::signed(2), StakingBalance::KtonBalance(100), 3), + Error::::InsufficientProposersBalance, + ); }); } #[test] fn accepted_spend_proposal_ignored_outside_spend_period() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( Origin::signed(0), StakingBalance::RingBalance(100), @@ -731,27 +761,51 @@ mod tests { >::on_finalize(1); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Treasury::pot::(), 100); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(3); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 100); }); } #[test] fn unused_pot_should_diminish() { new_test_ext().execute_with(|| { - let init_total_issuance = Ring::total_issuance(); + let init_total_ring_issuance = Ring::total_issuance(); + let init_total_kton_issuance = Kton::total_issuance(); + + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 100); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 50); + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 50); + + // kton Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Ring::total_issuance(), init_total_issuance + 100); + assert_eq!(Ring::total_issuance(), init_total_kton_issuance + 100); >::on_finalize(2); assert_eq!(Treasury::pot::(), 50); - assert_eq!(Ring::total_issuance(), init_total_issuance + 50); + assert_eq!(Ring::total_issuance(), init_total_kton_issuance + 50); }); } #[test] fn rejected_spend_proposal_ignored_on_spend_period() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( Origin::signed(0), StakingBalance::RingBalance(100), @@ -762,12 +816,27 @@ mod tests { >::on_finalize(2); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Treasury::pot::(), 50); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + + >::on_finalize(2); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 50); }); } #[test] fn reject_already_rejected_spend_proposal_fails() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend( @@ -780,6 +849,20 @@ mod tests { Treasury::reject_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex ); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + assert_noop!( + Treasury::reject_proposal(Origin::ROOT, 1), + Error::::InvalidProposalIndex + ); }); } @@ -806,6 +889,7 @@ mod tests { #[test] fn accept_already_rejected_spend_proposal_fails() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_ok!(Treasury::propose_spend( @@ -818,12 +902,27 @@ mod tests { Treasury::approve_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex ); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + assert_noop!( + Treasury::approve_proposal(Origin::ROOT, 1), + Error::::InvalidProposalIndex + ); }); } #[test] fn accepted_spend_proposal_enacted_on_spend_period() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); @@ -838,11 +937,29 @@ mod tests { assert_eq!(Ring::free_balance(&3), 100); assert_eq!(Treasury::pot::(), 0); }); + + // kton + new_test_ext().execute_with(|| { + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Kton::free_balance(&3), 100); + assert_eq!(Treasury::pot::(), 0); + }); } #[test] fn pot_underflow_should_not_diminish() { new_test_ext().execute_with(|| { + // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); assert_ok!(Treasury::propose_spend( @@ -859,6 +976,24 @@ mod tests { >::on_finalize(4); assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent assert_eq!(Treasury::pot::(), 25); // Pot has finally changed + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(150), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(3); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + let _ = Kton::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); + >::on_finalize(4); + assert_eq!(Kton::free_balance(&3), 150); // Fund has been spent + assert_eq!(Treasury::pot::(), 25); // Pot has finally changed }); } @@ -888,31 +1023,61 @@ mod tests { assert_eq!(Treasury::pot::(), 0); // Pot is emptied assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there }); + + new_test_ext().execute_with(|| { + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + let kton_treasury_balance = StakingBalance::KtonBalance(Kton::free_balance(&Treasury::account_id())); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), kton_treasury_balance, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(Treasury::pot::()), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(4); + assert_eq!(Treasury::pot::(), 0); // Pot is emptied + assert_eq!(Kton::free_balance(&Treasury::account_id()), 1); // but the account is still there + }); } // In case treasury account is not existing then it works fine. // This is usefull for chain that will just update runtime. #[test] fn inexisting_account_works() { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut tr = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut tk = frame_system::GenesisConfig::default().build_storage::().unwrap(); darwinia_ring::GenesisConfig:: { balances: vec![(0, 100), (1, 99), (2, 1)], vesting: vec![], } - .assimilate_storage(&mut t) + .assimilate_storage(&mut tr) .unwrap(); + + darwinia_kton::GenesisConfig:: { + balances: vec![(0, 100), (1, 99), (2, 1)], + vesting: vec![], + } + .assimilate_storage(&mut tk) + .unwrap(); + // Treasury genesis config is not build thus treasury account does not exist - let mut t: sp_io::TestExternalities = t.into(); + let mut tr: sp_io::TestExternalities = tr.into(); + let mut tk: sp_io::TestExternalities = tk.into(); - t.execute_with(|| { + tr.execute_with(|| { // Account does not exist assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); - assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); // Pot is empty assert_eq!(Treasury::pot::(), 0); - assert_eq!(Treasury::pot::(), 0); - assert_ok!(Treasury::propose_spend( Origin::signed(0), StakingBalance::RingBalance(99), @@ -941,5 +1106,40 @@ mod tests { assert_eq!(Treasury::pot::(), 0); // Pot has changed assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed }); + + tk.execute_with(|| { + // Account does not exist + assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); + + // Pot is empty + assert_eq!(Treasury::pot::(), 0); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(99), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(1), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(2); + // Pot hasn't changed + assert_eq!(Treasury::pot::(), 0); + // Balance of `3` hasn't changed + assert_eq!(Kton::free_balance(&3), 0); + + Kton::make_free_balance_be(&Treasury::account_id(), 100); + assert_eq!(Treasury::pot::(), 99); // Pot now contains funds + assert_eq!(Kton::free_balance(&Treasury::account_id()), 100); // Account does exist + + >::on_finalize(4); + + assert_eq!(Treasury::pot::(), 0); // Pot has changed + assert_eq!(Kton::free_balance(&3), 99); // Balance of `3` has changed + }); } } From e7eec5a57980dcb8a3f3900973a3341e36212d1a Mon Sep 17 00:00:00 2001 From: clearloop Date: Mon, 2 Mar 2020 00:06:13 +0800 Subject: [PATCH 12/19] add: separate treasury tests --- frame/treasury/README.adoc | 40 +++ frame/treasury/src/lib.rs | 598 +----------------------------------- frame/treasury/src/tests.rs | 590 +++++++++++++++++++++++++++++++++++ 3 files changed, 632 insertions(+), 596 deletions(-) create mode 100644 frame/treasury/README.adoc create mode 100644 frame/treasury/src/tests.rs diff --git a/frame/treasury/README.adoc b/frame/treasury/README.adoc new file mode 100644 index 000000000..e82fb7e84 --- /dev/null +++ b/frame/treasury/README.adoc @@ -0,0 +1,40 @@ +# Treasury Module + +The Treasury module provides a "pot" of funds that can be managed by stakeholders in the +system and a structure for making spending proposals from this pot. + +- `treasury::Trait` +- `Call` + +## Overview + +The Treasury Module itself provides the pot to store funds, and a means for stakeholders to +propose, approve, and deny expenditures. The chain will need to provide a method (e.g. +inflation, fees) for collecting funds. + +By way of example, the Council could vote to fund the Treasury with a portion of the block +reward and use the funds to pay developers. + +### Terminology + +- **Proposal:** A suggestion to allocate funds from the pot to a beneficiary. +- **Beneficiary:** An account who will receive the funds from a proposal iff +the proposal is approved. +- **Deposit:** Funds that a proposer must lock when making a proposal. The +deposit will be returned or slashed if the proposal is approved or rejected +respectively. +- **Pot:** Unspent funds accumulated by the treasury module. + +## Interface + +### Dispatchable Functions + +- `propose_spend` - Make a spending proposal and stake the required deposit. +- `set_pot` - Set the spendable balance of funds. +- `configure` - Configure the module's proposal requirements. +- `reject_proposal` - Reject a proposal, slashing the deposit. +- `approve_proposal` - Accept the proposal, returning the deposit. + +## GenesisConfig + +The Treasury module depends on the `GenesisConfig`. diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 388b406de..0a067f679 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -54,9 +54,9 @@ //! ## GenesisConfig //! //! The Treasury module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). - #![cfg_attr(not(feature = "std"), no_std)] - +#[cfg(test)] +mod tests; mod types { use crate::*; @@ -549,597 +549,3 @@ impl OnUnbalancedKton> for Module { Self::deposit_event(RawEvent::DepositKton(numeric_amount)); } } - -#[cfg(test)] -mod tests { - use crate::*; - use frame_support::{assert_noop, assert_ok, impl_outer_origin, parameter_types, weights::Weight}; - use sp_core::H256; - use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, IdentityLookup, OnFinalize}, - Perbill, - }; - - type Ring = darwinia_ring::Module; - type Kton = darwinia_kton::Module; - type Treasury = Module; - - impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} - } - - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); - } - impl frame_system::Trait for Test { - type Origin = Origin; - type Call = (); - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = (); - type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type Version = (); - type ModuleToIndex = (); - } - parameter_types! { - pub const ExistentialDeposit: u64 = 1; - pub const TransferFee: u64 = 0; - pub const CreationFee: u64 = 0; - } - impl darwinia_kton::Trait for Test { - type Balance = u64; - type Event = (); - type RingCurrency = Ring; - type TransferPayment = (); - type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - } - impl darwinia_ring::Trait for Test { - type Balance = u64; - type OnFreeBalanceZero = (); - type OnNewAccount = (); - type TransferPayment = (); - type DustRemoval = (); - type Event = (); - type ExistentialDeposit = ExistentialDeposit; - type TransferFee = TransferFee; - type CreationFee = CreationFee; - } - parameter_types! { - pub const ProposalBond: Permill = Permill::from_percent(5); - pub const RingProposalBondMinimum: u64 = 1; - pub const KtonProposalBondMinimum: u64 = 1; - pub const SpendPeriod: u64 = 2; - pub const Burn: Permill = Permill::from_percent(50); - } - impl Trait for Test { - type RingCurrency = Ring; - type KtonCurrency = Kton; - type ApproveOrigin = frame_system::EnsureRoot; - type RejectOrigin = frame_system::EnsureRoot; - type Event = (); - type KtonProposalRejection = (); - type RingProposalRejection = (); - type ProposalBond = ProposalBond; - type RingProposalBondMinimum = RingProposalBondMinimum; - type KtonProposalBondMinimum = KtonProposalBondMinimum; - type SpendPeriod = SpendPeriod; - type Burn = Burn; - } - - fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let _ = darwinia_ring::GenesisConfig:: { - // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], - vesting: vec![], - } - .assimilate_storage(&mut t); - let _ = darwinia_kton::GenesisConfig:: { - // Total issuance will be 200 with treasury account initialized at ED. - balances: vec![(0, 100), (1, 98), (2, 1)], - vesting: vec![], - } - .assimilate_storage(&mut t); - let _ = GenesisConfig::default().assimilate_storage::(&mut t); - t.into() - } - - #[test] - fn genesis_config_works() { - new_test_ext().execute_with(|| { - assert_eq!(Treasury::pot::(), 0); - assert_eq!(Treasury::pot::(), 0); - assert_eq!(Treasury::proposal_count(), 0); - }); - } - - #[test] - fn minting_works() { - new_test_ext().execute_with(|| { - // Check that accumulate works when we have Some value in Dummy already. - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - assert_eq!(Treasury::pot::(), 0); - - // Make sure kton and ring have different storages - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - }); - } - - /// min deposit is 0 now - #[test] - fn spend_proposal_takes_min_deposit() { - new_test_ext().execute_with(|| { - // ring - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(1), - 3 - )); - assert_eq!(Ring::free_balance(&0), 99); - assert_eq!(Ring::reserved_balance(&0), 1); - - // kton - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(1), - 3 - )); - assert_eq!(Kton::free_balance(&0), 99); - assert_eq!(Kton::reserved_balance(&0), 1); - }); - } - - #[test] - fn spend_proposal_takes_proportional_deposit() { - new_test_ext().execute_with(|| { - // ring - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_eq!(Ring::free_balance(&0), 95); - assert_eq!(Ring::reserved_balance(&0), 5); - - // kton - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_eq!(Kton::free_balance(&0), 95); - assert_eq!(Kton::reserved_balance(&0), 5); - }); - } - - #[test] - fn spend_proposal_fails_when_proposer_poor() { - new_test_ext().execute_with(|| { - // ring - assert_noop!( - Treasury::propose_spend(Origin::signed(2), StakingBalance::RingBalance(100), 3), - Error::::InsufficientProposersBalance, - ); - - // kton - assert_noop!( - Treasury::propose_spend(Origin::signed(2), StakingBalance::KtonBalance(100), 3), - Error::::InsufficientProposersBalance, - ); - }); - } - - #[test] - fn accepted_spend_proposal_ignored_outside_spend_period() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(1); - assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 100); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(3); - assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 100); - }); - } - - #[test] - fn unused_pot_should_diminish() { - new_test_ext().execute_with(|| { - let init_total_ring_issuance = Ring::total_issuance(); - let init_total_kton_issuance = Kton::total_issuance(); - - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 100); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 50); - assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 50); - - // kton - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Ring::total_issuance(), init_total_kton_issuance + 100); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 50); - assert_eq!(Ring::total_issuance(), init_total_kton_issuance + 50); - }); - } - - #[test] - fn rejected_spend_proposal_ignored_on_spend_period() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 50); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); - - >::on_finalize(2); - assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 50); - }); - } - - #[test] - fn reject_already_rejected_spend_proposal_fails() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!( - Treasury::reject_proposal(Origin::ROOT, 0), - Error::::InvalidProposalIndex - ); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); - assert_noop!( - Treasury::reject_proposal(Origin::ROOT, 1), - Error::::InvalidProposalIndex - ); - }); - } - - #[test] - fn reject_non_existant_spend_proposal_fails() { - new_test_ext().execute_with(|| { - assert_noop!( - Treasury::reject_proposal(Origin::ROOT, 0), - Error::::InvalidProposalIndex - ); - }); - } - - #[test] - fn accept_non_existant_spend_proposal_fails() { - new_test_ext().execute_with(|| { - assert_noop!( - Treasury::approve_proposal(Origin::ROOT, 0), - Error::::InvalidProposalIndex - ); - }); - } - - #[test] - fn accept_already_rejected_spend_proposal_fails() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - assert_noop!( - Treasury::approve_proposal(Origin::ROOT, 0), - Error::::InvalidProposalIndex - ); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); - assert_noop!( - Treasury::approve_proposal(Origin::ROOT, 1), - Error::::InvalidProposalIndex - ); - }); - } - - #[test] - fn accepted_spend_proposal_enacted_on_spend_period() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Ring::free_balance(&3), 100); - assert_eq!(Treasury::pot::(), 0); - }); - - // kton - new_test_ext().execute_with(|| { - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Kton::free_balance(&3), 100); - assert_eq!(Treasury::pot::(), 0); - }); - } - - #[test] - fn pot_underflow_should_not_diminish() { - new_test_ext().execute_with(|| { - // ring - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(150), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - - let _ = Ring::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); - >::on_finalize(4); - assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent - assert_eq!(Treasury::pot::(), 25); // Pot has finally changed - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(150), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(3); - assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - - let _ = Kton::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); - >::on_finalize(4); - assert_eq!(Kton::free_balance(&3), 150); // Fund has been spent - assert_eq!(Treasury::pot::(), 25); // Pot has finally changed - }); - } - - // Treasury account doesn't get deleted if amount approved to spend is all its free balance. - // i.e. pot should not include existential deposit needed for account survival. - #[test] - fn treasury_account_doesnt_get_deleted() { - new_test_ext().execute_with(|| { - Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - let ring_treasury_balance = StakingBalance::RingBalance(Ring::free_balance(&Treasury::account_id())); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), ring_treasury_balance, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(Treasury::pot::()), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(4); - assert_eq!(Treasury::pot::(), 0); // Pot is emptied - assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there - }); - - new_test_ext().execute_with(|| { - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - let kton_treasury_balance = StakingBalance::KtonBalance(Kton::free_balance(&Treasury::account_id())); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), kton_treasury_balance, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(Treasury::pot::()), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(4); - assert_eq!(Treasury::pot::(), 0); // Pot is emptied - assert_eq!(Kton::free_balance(&Treasury::account_id()), 1); // but the account is still there - }); - } - - // In case treasury account is not existing then it works fine. - // This is usefull for chain that will just update runtime. - #[test] - fn inexisting_account_works() { - let mut tr = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let mut tk = frame_system::GenesisConfig::default().build_storage::().unwrap(); - darwinia_ring::GenesisConfig:: { - balances: vec![(0, 100), (1, 99), (2, 1)], - vesting: vec![], - } - .assimilate_storage(&mut tr) - .unwrap(); - - darwinia_kton::GenesisConfig:: { - balances: vec![(0, 100), (1, 99), (2, 1)], - vesting: vec![], - } - .assimilate_storage(&mut tk) - .unwrap(); - - // Treasury genesis config is not build thus treasury account does not exist - let mut tr: sp_io::TestExternalities = tr.into(); - let mut tk: sp_io::TestExternalities = tk.into(); - - tr.execute_with(|| { - // Account does not exist - assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); - - // Pot is empty - assert_eq!(Treasury::pot::(), 0); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(99), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(1), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(2); - // Pot hasn't changed - assert_eq!(Treasury::pot::(), 0); - // Balance of `3` hasn't changed - assert_eq!(Ring::free_balance(&3), 0); - - Ring::make_free_balance_be(&Treasury::account_id(), 100); - assert_eq!(Treasury::pot::(), 99); // Pot now contains funds - assert_eq!(Ring::free_balance(&Treasury::account_id()), 100); // Account does exist - - >::on_finalize(4); - - assert_eq!(Treasury::pot::(), 0); // Pot has changed - assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed - }); - - tk.execute_with(|| { - // Account does not exist - assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); - - // Pot is empty - assert_eq!(Treasury::pot::(), 0); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(99), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(1), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(2); - // Pot hasn't changed - assert_eq!(Treasury::pot::(), 0); - // Balance of `3` hasn't changed - assert_eq!(Kton::free_balance(&3), 0); - - Kton::make_free_balance_be(&Treasury::account_id(), 100); - assert_eq!(Treasury::pot::(), 99); // Pot now contains funds - assert_eq!(Kton::free_balance(&Treasury::account_id()), 100); // Account does exist - - >::on_finalize(4); - - assert_eq!(Treasury::pot::(), 0); // Pot has changed - assert_eq!(Kton::free_balance(&3), 99); // Balance of `3` has changed - }); - } -} diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs new file mode 100644 index 000000000..67b17ec21 --- /dev/null +++ b/frame/treasury/src/tests.rs @@ -0,0 +1,590 @@ +use crate::*; +use frame_support::{assert_noop, assert_ok, impl_outer_origin, parameter_types, weights::Weight}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup, OnFinalize}, + Perbill, +}; + +type Ring = darwinia_ring::Module; +type Kton = darwinia_kton::Module; +type Treasury = Module; + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl frame_system::Trait for Test { + type Origin = Origin; + type Call = (); + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; + pub const TransferFee: u64 = 0; + pub const CreationFee: u64 = 0; +} +impl darwinia_kton::Trait for Test { + type Balance = u64; + type Event = (); + type RingCurrency = Ring; + type TransferPayment = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; +} +impl darwinia_ring::Trait for Test { + type Balance = u64; + type OnFreeBalanceZero = (); + type OnNewAccount = (); + type TransferPayment = (); + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type TransferFee = TransferFee; + type CreationFee = CreationFee; +} +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const RingProposalBondMinimum: u64 = 1; + pub const KtonProposalBondMinimum: u64 = 1; + pub const SpendPeriod: u64 = 2; + pub const Burn: Permill = Permill::from_percent(50); +} +impl Trait for Test { + type RingCurrency = Ring; + type KtonCurrency = Kton; + type ApproveOrigin = frame_system::EnsureRoot; + type RejectOrigin = frame_system::EnsureRoot; + type Event = (); + type KtonProposalRejection = (); + type RingProposalRejection = (); + type ProposalBond = ProposalBond; + type RingProposalBondMinimum = RingProposalBondMinimum; + type KtonProposalBondMinimum = KtonProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; +} + +fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let _ = darwinia_ring::GenesisConfig:: { + // Total issuance will be 200 with treasury account initialized at ED. + balances: vec![(0, 100), (1, 98), (2, 1)], + vesting: vec![], + } + .assimilate_storage(&mut t); + let _ = darwinia_kton::GenesisConfig:: { + // Total issuance will be 200 with treasury account initialized at ED. + balances: vec![(0, 100), (1, 98), (2, 1)], + vesting: vec![], + } + .assimilate_storage(&mut t); + let _ = GenesisConfig::default().assimilate_storage::(&mut t); + t.into() +} + +#[test] +fn genesis_config_works() { + new_test_ext().execute_with(|| { + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::proposal_count(), 0); + }); +} + +#[test] +fn minting_works() { + new_test_ext().execute_with(|| { + // Check that accumulate works when we have Some value in Dummy already. + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 0); + + // Make sure kton and ring have different storages + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + }); +} + +/// min deposit is 0 now +#[test] +fn spend_proposal_takes_min_deposit() { + new_test_ext().execute_with(|| { + // ring + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(1), + 3 + )); + assert_eq!(Ring::free_balance(&0), 99); + assert_eq!(Ring::reserved_balance(&0), 1); + + // kton + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(1), + 3 + )); + assert_eq!(Kton::free_balance(&0), 99); + assert_eq!(Kton::reserved_balance(&0), 1); + }); +} + +#[test] +fn spend_proposal_takes_proportional_deposit() { + new_test_ext().execute_with(|| { + // ring + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_eq!(Ring::free_balance(&0), 95); + assert_eq!(Ring::reserved_balance(&0), 5); + + // kton + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_eq!(Kton::free_balance(&0), 95); + assert_eq!(Kton::reserved_balance(&0), 5); + }); +} + +#[test] +fn spend_proposal_fails_when_proposer_poor() { + new_test_ext().execute_with(|| { + // ring + assert_noop!( + Treasury::propose_spend(Origin::signed(2), StakingBalance::RingBalance(100), 3), + Error::::InsufficientProposersBalance, + ); + + // kton + assert_noop!( + Treasury::propose_spend(Origin::signed(2), StakingBalance::KtonBalance(100), 3), + Error::::InsufficientProposersBalance, + ); + }); +} + +#[test] +fn accepted_spend_proposal_ignored_outside_spend_period() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(1); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 100); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(3); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 100); + }); +} + +#[test] +fn unused_pot_should_diminish() { + new_test_ext().execute_with(|| { + let init_total_ring_issuance = Ring::total_issuance(); + let init_total_kton_issuance = Kton::total_issuance(); + + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 100); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 50); + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 50); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Kton::total_issuance(), init_total_kton_issuance + 100); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 50); + assert_eq!(Kton::total_issuance(), init_total_kton_issuance + 50); + }); +} + +#[test] +fn rejected_spend_proposal_ignored_on_spend_period() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 50); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + + >::on_finalize(2); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 50); + }); +} + +#[test] +fn reject_already_rejected_spend_proposal_fails() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_noop!( + Treasury::reject_proposal(Origin::ROOT, 0), + Error::::InvalidProposalIndex + ); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + assert_noop!( + Treasury::reject_proposal(Origin::ROOT, 1), + Error::::InvalidProposalIndex + ); + }); +} + +#[test] +fn reject_non_existant_spend_proposal_fails() { + new_test_ext().execute_with(|| { + assert_noop!( + Treasury::reject_proposal(Origin::ROOT, 0), + Error::::InvalidProposalIndex + ); + }); +} + +#[test] +fn accept_non_existant_spend_proposal_fails() { + new_test_ext().execute_with(|| { + assert_noop!( + Treasury::approve_proposal(Origin::ROOT, 0), + Error::::InvalidProposalIndex + ); + }); +} + +#[test] +fn accept_already_rejected_spend_proposal_fails() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_noop!( + Treasury::approve_proposal(Origin::ROOT, 0), + Error::::InvalidProposalIndex + ); + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + assert_noop!( + Treasury::approve_proposal(Origin::ROOT, 1), + Error::::InvalidProposalIndex + ); + }); +} + +#[test] +fn accepted_spend_proposal_enacted_on_spend_period() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 100); + assert_eq!(Treasury::pot::(), 0); + }); + + // kton + new_test_ext().execute_with(|| { + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(100), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Kton::free_balance(&3), 100); + assert_eq!(Treasury::pot::(), 0); + }); +} + +#[test] +fn pot_underflow_should_not_diminish() { + new_test_ext().execute_with(|| { + // ring + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(150), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + let _ = Ring::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); + >::on_finalize(4); + assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent + assert_eq!(Treasury::pot::(), 25); // Pot has finally changed + + // kton + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(150), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(3); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + let _ = Kton::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); + >::on_finalize(4); + assert_eq!(Kton::free_balance(&3), 150); // Fund has been spent + assert_eq!(Treasury::pot::(), 25); // Pot has finally changed + }); +} + +// Treasury account doesn't get deleted if amount approved to spend is all its free balance. +// i.e. pot should not include existential deposit needed for account survival. +#[test] +fn treasury_account_doesnt_get_deleted() { + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + let ring_treasury_balance = StakingBalance::RingBalance(Ring::free_balance(&Treasury::account_id())); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), ring_treasury_balance, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(Treasury::pot::()), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(4); + assert_eq!(Treasury::pot::(), 0); // Pot is emptied + assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there + }); + + new_test_ext().execute_with(|| { + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + let kton_treasury_balance = StakingBalance::KtonBalance(Kton::free_balance(&Treasury::account_id())); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), kton_treasury_balance, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(Treasury::pot::()), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(4); + assert_eq!(Treasury::pot::(), 0); // Pot is emptied + assert_eq!(Kton::free_balance(&Treasury::account_id()), 1); // but the account is still there + }); +} + +// In case treasury account is not existing then it works fine. +// This is usefull for chain that will just update runtime. +#[test] +fn inexisting_account_works() { + let mut tr = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut tk = frame_system::GenesisConfig::default().build_storage::().unwrap(); + darwinia_ring::GenesisConfig:: { + balances: vec![(0, 100), (1, 99), (2, 1)], + vesting: vec![], + } + .assimilate_storage(&mut tr) + .unwrap(); + + darwinia_kton::GenesisConfig:: { + balances: vec![(0, 100), (1, 99), (2, 1)], + vesting: vec![], + } + .assimilate_storage(&mut tk) + .unwrap(); + + // Treasury genesis config is not build thus treasury account does not exist + let mut tr: sp_io::TestExternalities = tr.into(); + let mut tk: sp_io::TestExternalities = tk.into(); + + tr.execute_with(|| { + // Account does not exist + assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); + + // Pot is empty + assert_eq!(Treasury::pot::(), 0); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(99), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::RingBalance(1), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(2); + // Pot hasn't changed + assert_eq!(Treasury::pot::(), 0); + // Balance of `3` hasn't changed + assert_eq!(Ring::free_balance(&3), 0); + + Ring::make_free_balance_be(&Treasury::account_id(), 100); + assert_eq!(Treasury::pot::(), 99); // Pot now contains funds + assert_eq!(Ring::free_balance(&Treasury::account_id()), 100); // Account does exist + + >::on_finalize(4); + + assert_eq!(Treasury::pot::(), 0); // Pot has changed + assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed + }); + + tk.execute_with(|| { + // Account does not exist + assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); + + // Pot is empty + assert_eq!(Treasury::pot::(), 0); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(99), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::propose_spend( + Origin::signed(0), + StakingBalance::KtonBalance(1), + 3 + )); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); + + >::on_finalize(2); + // Pot hasn't changed + assert_eq!(Treasury::pot::(), 0); + // Balance of `3` hasn't changed + assert_eq!(Kton::free_balance(&3), 0); + + Kton::make_free_balance_be(&Treasury::account_id(), 100); + assert_eq!(Treasury::pot::(), 99); // Pot now contains funds + assert_eq!(Kton::free_balance(&Treasury::account_id()), 100); // Account does exist + + >::on_finalize(4); + + assert_eq!(Treasury::pot::(), 0); // Pot has changed + assert_eq!(Kton::free_balance(&3), 99); // Balance of `3` has changed + }); +} From 5a175b262fdd6d275b68c81e02d4372628987076 Mon Sep 17 00:00:00 2001 From: clearloop Date: Mon, 2 Mar 2020 19:10:24 +0800 Subject: [PATCH 13/19] remove: StakingBalance enum in treasury --- frame/treasury/src/lib.rs | 270 +++++++++++--------------------- frame/treasury/src/tests.rs | 304 +++++++----------------------------- 2 files changed, 147 insertions(+), 427 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 0a067f679..3fdc3d329 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -66,15 +66,14 @@ mod types { pub type KtonBalance = as Currency>>::Balance; pub type KtonPositiveImbalance = as Currency>>::PositiveImbalance; pub type KtonNegativeImbalance = as Currency>>::NegativeImbalance; - pub type StakingBalanceT = StakingBalance, KtonBalance>; type AccountId = ::AccountId; type RingCurrency = ::RingCurrency; type KtonCurrency = ::KtonCurrency; } +// third-parity use codec::{Decode, Encode}; -use darwinia_support::OnUnbalancedKton; use frame_support::{ decl_error, decl_event, decl_module, decl_storage, ensure, print, traits::{Currency, ExistenceRequirement, Get, Imbalance, OnUnbalanced, ReservableCurrency, WithdrawReason}, @@ -90,6 +89,9 @@ use sp_runtime::{ use sp_std::prelude::*; use types::*; +// custom +use darwinia_support::OnUnbalancedKton; + const MODULE_ID: ModuleId = ModuleId(*b"py/trsry"); pub trait Trait: frame_system::Trait { @@ -129,32 +131,6 @@ pub trait Trait: frame_system::Trait { type ProposalIndex = u32; -/// Reversed for manually usage -#[allow(unused)] -enum Funds { - Kton, - Ring, -} - -/// To unify *Ring* and *Kton* balances. Ref to the solution at -/// [`darwinia_staking`](../darwinia_staking/enum.StakingBalance.html), -/// keep the `StakingBalance` name for upgrade usages. -#[derive(Copy, Clone, Encode, Decode, Eq, Ord, RuntimeDebug, PartialEq, PartialOrd)] -pub enum StakingBalance { - RingBalance(RingBalance), - KtonBalance(KtonBalance), -} - -impl StakingBalance { - fn ring(v: RingBalance) -> Self { - StakingBalance::RingBalance(v) - } - - fn kton(v: KtonBalance) -> Self { - StakingBalance::KtonBalance(v) - } -} - decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Fraction of a proposal's value that should be bonded in order to place the proposal. @@ -187,30 +163,28 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedNormal(500_000)] fn propose_spend( origin, - value: StakingBalanceT, + ring_value: RingBalance, + kton_value: KtonBalance, beneficiary: ::Source ) { let proposer = ensure_signed(origin)?; let beneficiary = T::Lookup::lookup(beneficiary)?; - let bond = Self::calculate_bond(value); - match bond { - StakingBalance::KtonBalance(bond) => { - T::KtonCurrency::reserve(&proposer, bond) - .map_err(|_| >::InsufficientProposersBalance)?; - }, - StakingBalance::RingBalance(bond) => { - T::RingCurrency::reserve(&proposer, bond) - .map_err(|_| >::InsufficientProposersBalance)?; - } - } + let (ring_bond, kton_bond) = Self::calculate_bonds(ring_value, kton_value); + + T::RingCurrency::reserve(&proposer, ring_bond) + .map_err(|_| >::InsufficientProposersBalance)?; + T::KtonCurrency::reserve(&proposer, kton_bond) + .map_err(|_| >::InsufficientProposersBalance)?; let c = Self::proposal_count(); ProposalCount::put(c + 1); >::insert(c, Proposal { proposer, beneficiary, - bond, - value, + ring_bond, + ring_value, + kton_bond, + kton_value, }); Self::deposit_event(RawEvent::Proposed(c)); @@ -227,20 +201,15 @@ decl_module! { fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::RejectOrigin::ensure_origin(origin)?; let proposal = >::take(&proposal_id).ok_or(>::InvalidProposalIndex)?; + let ring_bond = proposal.ring_bond; + let kton_bond = proposal.kton_bond; + let imbalance_ring = T::RingCurrency::slash_reserved(&proposal.proposer, ring_bond).0; + let imbalance_kton = T::KtonCurrency::slash_reserved(&proposal.proposer, kton_bond).0; - let value = proposal.bond; - match value { - StakingBalance::KtonBalance(value) => { - let imbalance = T::KtonCurrency::slash_reserved(&proposal.proposer, value).0; - T::KtonProposalRejection::on_unbalanced(imbalance); - }, - StakingBalance::RingBalance(value) => { - let imbalance = T::RingCurrency::slash_reserved(&proposal.proposer, value).0; - T::RingProposalRejection::on_unbalanced(imbalance); - } - } + T::RingProposalRejection::on_unbalanced(imbalance_ring); + T::KtonProposalRejection::on_unbalanced(imbalance_kton); - Self::deposit_event(Event::::Rejected(proposal_id, value)); + Self::deposit_event(Event::::Rejected(proposal_id, ring_bond, kton_bond)); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -254,9 +223,7 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedOperational(100_000)] fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::ApproveOrigin::ensure_origin(origin)?; - ensure!(>::exists(proposal_id), >::InvalidProposalIndex); - Approvals::mutate(|v| v.push(proposal_id)); } @@ -264,8 +231,7 @@ decl_module! { fn on_finalize(n: T::BlockNumber) { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { - Self::spend_funds(Funds::Ring); - Self::spend_funds(Funds::Kton); + Self::spend_funds(); } } } @@ -273,12 +239,14 @@ decl_module! { /// A spending proposal. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, PartialEq, Eq, sp_runtime::RuntimeDebug)] -pub struct Proposal { +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct Proposal { proposer: AccountId, - value: StakingBalance, beneficiary: AccountId, - bond: StakingBalance, + ring_bond: RingBalance, + ring_value: RingBalance, + kton_bond: KtonBalance, + kton_value: KtonBalance, } decl_storage! { @@ -287,7 +255,7 @@ decl_storage! { ProposalCount get(fn proposal_count): ProposalIndex; /// Proposals that have been made. - Proposals get(fn proposals): map ProposalIndex => Option>>; + Proposals get(fn proposals): map ProposalIndex => Option, KtonBalance>>; /// Proposal indices that have been approved but not yet awarded. Approvals get(fn approvals): Vec; @@ -309,7 +277,6 @@ decl_storage! { } decl_event!( - /// TODO: Events below needs to replace RingBalance to StakingBalance pub enum Event where ::AccountId, @@ -319,15 +286,15 @@ decl_event!( /// New proposal. Proposed(ProposalIndex), /// We have ended a spend period and will now allocate funds. - Spending(StakingBalance), + Spending(RingBalance, KtonBalance), /// Some funds have been allocated. - Awarded(ProposalIndex, StakingBalance, AccountId), + Awarded(ProposalIndex, RingBalance, KtonBalance, AccountId), /// A proposal was rejected; funds were slashed. - Rejected(ProposalIndex, StakingBalance), + Rejected(ProposalIndex, RingBalance, KtonBalance), /// Some of our funds have been burnt. - Burnt(StakingBalance), + Burnt(RingBalance, KtonBalance), /// Spending has finished; this is the amount that rolls over until next spend. - Rollover(StakingBalance), + Rollover(RingBalance, KtonBalance), /// Some *Ring* have been deposited. DepositRing(RingBalance), /// Some *Kton* have been deposited. @@ -357,24 +324,24 @@ impl Module { } /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: StakingBalanceT) -> StakingBalanceT { - match value { - StakingBalance::KtonBalance(value) => { - StakingBalance::kton(T::KtonProposalBondMinimum::get().max(T::ProposalBond::get() * value)) - } - StakingBalance::RingBalance(value) => { - StakingBalance::ring(T::RingProposalBondMinimum::get().max(T::ProposalBond::get() * value)) - } - } + fn calculate_bonds(ring: RingBalance, kton: KtonBalance) -> (RingBalance, KtonBalance) { + ( + T::RingProposalBondMinimum::get().max(T::ProposalBond::get() * ring), + T::KtonProposalBondMinimum::get().max(T::ProposalBond::get() * kton), + ) } - /// TODO: Need a pre-logic to infer what specific to do with kton or ring. - fn spend_kton_funds() { - let mut budget_remaining = Self::pot::(); - Self::deposit_event(RawEvent::Spending(StakingBalance::kton(budget_remaining))); + // Spend some money! + fn spend_funds() { + let mut budget_remaining_ring = Self::pot::(); + let mut budget_remaining_kton = Self::pot::(); + let mut imbalance_ring = >::zero(); + let mut imbalance_kton = >::zero(); + let mut should_burn_ring = true; + let mut should_burn_kton = true; + + Self::deposit_event(RawEvent::Spending(budget_remaining_ring, budget_remaining_kton)); - let mut missed_any = false; - let mut imbalance = >::zero(); Approvals::mutate(|v| { v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. @@ -384,40 +351,47 @@ impl Module { } let p = option_proposal.unwrap(); - match (p.value, p.bond) { - (StakingBalance::KtonBalance(value), StakingBalance::KtonBalance(bond)) => { - if value > budget_remaining { - missed_any = true; - return true; - } - - budget_remaining -= value; - >::remove(index); - - // return their deposit. - let _ = T::KtonCurrency::unreserve(&p.proposer, bond); - - // provide the allocation. - imbalance.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, value)); - - Self::deposit_event(RawEvent::Awarded( - index, - StakingBalance::KtonBalance(value), - p.beneficiary, - )); - false + if p.ring_value > budget_remaining_ring || p.kton_value > budget_remaining_kton { + if p.ring_value > budget_remaining_ring { + should_burn_ring = false; } - _ => true, + + if p.kton_value > budget_remaining_kton { + should_burn_kton = false; + } + + return true; } + + budget_remaining_ring -= p.ring_value; + budget_remaining_kton -= p.kton_value; + >::remove(index); + + // return their deposit. + let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); + let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); + + // provide the allocation. + imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); + imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); + + Self::deposit_event(RawEvent::Awarded(index, p.ring_value, p.kton_value, p.beneficiary)); + false }); }); - if !missed_any { + // burn balances + if should_burn_kton { + let burn = (T::Burn::get() * budget_remaining_kton).min(budget_remaining_kton); + budget_remaining_kton -= burn; + imbalance_kton.subsume(T::KtonCurrency::burn(burn)); + } + + if should_burn_ring { // burn some proportion of the remaining budget if we run a surplus. - let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); - budget_remaining -= burn; - imbalance.subsume(T::KtonCurrency::burn(burn)); - Self::deposit_event(RawEvent::Burnt(StakingBalance::KtonBalance(burn))) + let burn = (T::Burn::get() * budget_remaining_ring).min(budget_remaining_ring); + budget_remaining_ring -= burn; + imbalance_ring.subsume(T::RingCurrency::burn(burn)); } // Must never be an error, but better to be safe. @@ -426,7 +400,7 @@ impl Module { // Thus account is kept alive; qed; if let Err(problem) = T::KtonCurrency::settle( &Self::account_id(), - imbalance, + imbalance_kton, WithdrawReason::Transfer.into(), ExistenceRequirement::KeepAlive, ) { @@ -435,67 +409,9 @@ impl Module { drop(problem); } - Self::deposit_event(RawEvent::Rollover(StakingBalance::KtonBalance(budget_remaining))); - } - - fn spend_ring_funds() { - let mut budget_remaining = Self::pot::(); - Self::deposit_event(RawEvent::Spending(StakingBalance::RingBalance(budget_remaining))); - - let mut missed_any = false; - let mut imbalance = >::zero(); - Approvals::mutate(|v| { - v.retain(|&index| { - // Should always be true, but shouldn't panic if false or we're screwed. - let option_proposal = Self::proposals(index); - if option_proposal.is_none() { - return false; - } - - let p = option_proposal.unwrap(); - match (p.value, p.bond) { - (StakingBalance::RingBalance(value), StakingBalance::RingBalance(bond)) => { - if value > budget_remaining { - missed_any = true; - return true; - } - - budget_remaining -= value; - >::remove(index); - - // return their deposit. - let _ = T::RingCurrency::unreserve(&p.proposer, bond); - - // provide the allocation. - imbalance.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, value)); - - Self::deposit_event(RawEvent::Awarded( - index, - StakingBalance::RingBalance(value), - p.beneficiary, - )); - false - } - _ => true, - } - }); - }); - - if !missed_any { - // burn some proportion of the remaining budget if we run a surplus. - let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); - budget_remaining -= burn; - imbalance.subsume(T::RingCurrency::burn(burn)); - Self::deposit_event(RawEvent::Burnt(StakingBalance::RingBalance(burn))) - } - - // Must never be an error, but better to be safe. - // proof: budget_remaining is account free balance minus ED; - // Thus we can't spend more than account free balance minus ED; - // Thus account is kept alive; qed; if let Err(problem) = T::RingCurrency::settle( &Self::account_id(), - imbalance, + imbalance_ring, WithdrawReason::Transfer.into(), ExistenceRequirement::KeepAlive, ) { @@ -504,15 +420,7 @@ impl Module { drop(problem); } - Self::deposit_event(RawEvent::Rollover(StakingBalance::RingBalance(budget_remaining))); - } - - // Spend some money! - fn spend_funds(fund: Funds) { - match fund { - Funds::Kton => Self::spend_kton_funds(), - Funds::Ring => Self::spend_ring_funds(), - } + Self::deposit_event(RawEvent::Rollover(budget_remaining_ring, budget_remaining_kton)); } /// Return the amount of money in the pot. diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 67b17ec21..2cf585dba 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -132,22 +132,10 @@ fn minting_works() { #[test] fn spend_proposal_takes_min_deposit() { new_test_ext().execute_with(|| { - // ring - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(1), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 1, 3)); assert_eq!(Ring::free_balance(&0), 99); - assert_eq!(Ring::reserved_balance(&0), 1); - - // kton - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(1), - 3 - )); assert_eq!(Kton::free_balance(&0), 99); + assert_eq!(Ring::reserved_balance(&0), 1); assert_eq!(Kton::reserved_balance(&0), 1); }); } @@ -155,21 +143,9 @@ fn spend_proposal_takes_min_deposit() { #[test] fn spend_proposal_takes_proportional_deposit() { new_test_ext().execute_with(|| { - // ring - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_eq!(Ring::free_balance(&0), 95); assert_eq!(Ring::reserved_balance(&0), 5); - - // kton - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); assert_eq!(Kton::free_balance(&0), 95); assert_eq!(Kton::reserved_balance(&0), 5); }); @@ -180,13 +156,7 @@ fn spend_proposal_fails_when_proposer_poor() { new_test_ext().execute_with(|| { // ring assert_noop!( - Treasury::propose_spend(Origin::signed(2), StakingBalance::RingBalance(100), 3), - Error::::InsufficientProposersBalance, - ); - - // kton - assert_noop!( - Treasury::propose_spend(Origin::signed(2), StakingBalance::KtonBalance(100), 3), + Treasury::propose_spend(Origin::signed(2), 100, 100, 3), Error::::InsufficientProposersBalance, ); }); @@ -195,30 +165,16 @@ fn spend_proposal_fails_when_proposer_poor() { #[test] fn accepted_spend_proposal_ignored_outside_spend_period() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(1); assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 100); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(3); assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 100); assert_eq!(Treasury::pot::(), 100); }); } @@ -229,20 +185,16 @@ fn unused_pot_should_diminish() { let init_total_ring_issuance = Ring::total_issuance(); let init_total_kton_issuance = Kton::total_issuance(); - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 100); - - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 50); - assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 50); - - // kton Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 100); assert_eq!(Kton::total_issuance(), init_total_kton_issuance + 100); >::on_finalize(2); + assert_eq!(Treasury::pot::(), 50); assert_eq!(Treasury::pot::(), 50); + assert_eq!(Ring::total_issuance(), init_total_ring_issuance + 50); assert_eq!(Kton::total_issuance(), init_total_kton_issuance + 50); }); } @@ -250,31 +202,16 @@ fn unused_pot_should_diminish() { #[test] fn rejected_spend_proposal_ignored_on_spend_period() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Ring::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 50); - - // kton Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 50); assert_eq!(Treasury::pot::(), 50); }); } @@ -282,33 +219,15 @@ fn rejected_spend_proposal_ignored_on_spend_period() { #[test] fn reject_already_rejected_spend_proposal_fails() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); assert_noop!( Treasury::reject_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex ); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); - assert_noop!( - Treasury::reject_proposal(Origin::ROOT, 1), - Error::::InvalidProposalIndex - ); }); } @@ -322,82 +241,36 @@ fn reject_non_existant_spend_proposal_fails() { }); } -#[test] -fn accept_non_existant_spend_proposal_fails() { - new_test_ext().execute_with(|| { - assert_noop!( - Treasury::approve_proposal(Origin::ROOT, 0), - Error::::InvalidProposalIndex - ); - }); -} - #[test] fn accept_already_rejected_spend_proposal_fails() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); assert_noop!( Treasury::approve_proposal(Origin::ROOT, 0), Error::::InvalidProposalIndex ); - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); - assert_ok!(Treasury::reject_proposal(Origin::ROOT, 1)); - assert_noop!( - Treasury::approve_proposal(Origin::ROOT, 1), - Error::::InvalidProposalIndex - ); }); } #[test] fn accepted_spend_proposal_enacted_on_spend_period() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(100), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Ring::free_balance(&3), 100); - assert_eq!(Treasury::pot::(), 0); - }); - - // kton - new_test_ext().execute_with(|| { Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(100), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 100); assert_eq!(Kton::free_balance(&3), 100); + assert_eq!(Treasury::pot::(), 0); assert_eq!(Treasury::pot::(), 0); }); } @@ -405,40 +278,24 @@ fn accepted_spend_proposal_enacted_on_spend_period() { #[test] fn pot_underflow_should_not_diminish() { new_test_ext().execute_with(|| { - // ring Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(150), - 3 - )); + assert_eq!(Treasury::pot::(), 100); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 150, 150, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed - - let _ = Ring::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); - >::on_finalize(4); - assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent - assert_eq!(Treasury::pot::(), 25); // Pot has finally changed - - // kton - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(150), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(3); assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + let _ = Ring::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); let _ = Kton::deposit_into_existing(&Treasury::account_id(), 100).unwrap(); + >::on_finalize(4); + assert_eq!(Ring::free_balance(&3), 150); // Fund has been spent assert_eq!(Kton::free_balance(&3), 150); // Fund has been spent + assert_eq!(Treasury::pot::(), 25); // Pot has finally changed assert_eq!(Treasury::pot::(), 25); // Pot has finally changed }); } @@ -449,47 +306,38 @@ fn pot_underflow_should_not_diminish() { fn treasury_account_doesnt_get_deleted() { new_test_ext().execute_with(|| { Ring::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - let ring_treasury_balance = StakingBalance::RingBalance(Ring::free_balance(&Treasury::account_id())); + Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_ok!(Treasury::propose_spend(Origin::signed(0), ring_treasury_balance, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); - >::on_finalize(2); - assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed + let ring_treasury_balance = Ring::free_balance(&Treasury::account_id()); + let kton_treasury_balance = Kton::free_balance(&Treasury::account_id()); assert_ok!(Treasury::propose_spend( Origin::signed(0), - StakingBalance::RingBalance(Treasury::pot::()), + ring_treasury_balance, + kton_treasury_balance, 3 )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - - >::on_finalize(4); - assert_eq!(Treasury::pot::(), 0); // Pot is emptied - assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there - }); - - new_test_ext().execute_with(|| { - Kton::make_free_balance_be(&Treasury::account_id(), 101); - assert_eq!(Treasury::pot::(), 100); - let kton_treasury_balance = StakingBalance::KtonBalance(Kton::free_balance(&Treasury::account_id())); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), kton_treasury_balance, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); >::on_finalize(2); + assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed assert_eq!(Treasury::pot::(), 100); // Pot hasn't changed assert_ok!(Treasury::propose_spend( Origin::signed(0), - StakingBalance::KtonBalance(Treasury::pot::()), + Treasury::pot::(), + Treasury::pot::(), 3 )); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); >::on_finalize(4); + assert_eq!(Treasury::pot::(), 0); // Pot is emptied assert_eq!(Treasury::pot::(), 0); // Pot is emptied + assert_eq!(Ring::free_balance(&Treasury::account_id()), 1); // but the account is still there assert_eq!(Kton::free_balance(&Treasury::account_id()), 1); // but the account is still there }); } @@ -498,93 +346,57 @@ fn treasury_account_doesnt_get_deleted() { // This is usefull for chain that will just update runtime. #[test] fn inexisting_account_works() { - let mut tr = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let mut tk = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); darwinia_ring::GenesisConfig:: { balances: vec![(0, 100), (1, 99), (2, 1)], vesting: vec![], } - .assimilate_storage(&mut tr) + .assimilate_storage(&mut t) .unwrap(); darwinia_kton::GenesisConfig:: { balances: vec![(0, 100), (1, 99), (2, 1)], vesting: vec![], } - .assimilate_storage(&mut tk) + .assimilate_storage(&mut t) .unwrap(); // Treasury genesis config is not build thus treasury account does not exist - let mut tr: sp_io::TestExternalities = tr.into(); - let mut tk: sp_io::TestExternalities = tk.into(); - - tr.execute_with(|| { + let mut t: sp_io::TestExternalities = t.into(); + t.execute_with(|| { // Account does not exist assert_eq!(Ring::free_balance(&Treasury::account_id()), 0); + assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); // Pot is empty assert_eq!(Treasury::pot::(), 0); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(99), - 3 - )); + assert_eq!(Treasury::pot::(), 0); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 99, 99, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::RingBalance(1), - 3 - )); + assert_ok!(Treasury::propose_spend(Origin::signed(0), 1, 1, 3)); assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); >::on_finalize(2); // Pot hasn't changed assert_eq!(Treasury::pot::(), 0); - // Balance of `3` hasn't changed - assert_eq!(Ring::free_balance(&3), 0); - - Ring::make_free_balance_be(&Treasury::account_id(), 100); - assert_eq!(Treasury::pot::(), 99); // Pot now contains funds - assert_eq!(Ring::free_balance(&Treasury::account_id()), 100); // Account does exist - - >::on_finalize(4); - - assert_eq!(Treasury::pot::(), 0); // Pot has changed - assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed - }); - - tk.execute_with(|| { - // Account does not exist - assert_eq!(Kton::free_balance(&Treasury::account_id()), 0); - - // Pot is empty assert_eq!(Treasury::pot::(), 0); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(99), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - assert_ok!(Treasury::propose_spend( - Origin::signed(0), - StakingBalance::KtonBalance(1), - 3 - )); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 1)); - >::on_finalize(2); - // Pot hasn't changed - assert_eq!(Treasury::pot::(), 0); // Balance of `3` hasn't changed + assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 0); + Ring::make_free_balance_be(&Treasury::account_id(), 100); Kton::make_free_balance_be(&Treasury::account_id(), 100); + assert_eq!(Treasury::pot::(), 99); // Pot now contains funds assert_eq!(Treasury::pot::(), 99); // Pot now contains funds + assert_eq!(Ring::free_balance(&Treasury::account_id()), 100); // Account does exist assert_eq!(Kton::free_balance(&Treasury::account_id()), 100); // Account does exist >::on_finalize(4); + assert_eq!(Treasury::pot::(), 0); // Pot has changed assert_eq!(Treasury::pot::(), 0); // Pot has changed + assert_eq!(Ring::free_balance(&3), 99); // Balance of `3` has changed assert_eq!(Kton::free_balance(&3), 99); // Balance of `3` has changed }); } From a11cf71c51dd6d9961fd6896ca225dd7ad81c91a Mon Sep 17 00:00:00 2001 From: clearloop Date: Mon, 2 Mar 2020 21:55:50 +0800 Subject: [PATCH 14/19] fix: no spent no burn --- frame/treasury/src/lib.rs | 82 ++++++++++++++++++++----------------- frame/treasury/src/tests.rs | 23 ++++++++++- 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 3fdc3d329..1c323b0cd 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -181,10 +181,10 @@ decl_module! { >::insert(c, Proposal { proposer, beneficiary, - ring_bond, ring_value, - kton_bond, + ring_bond, kton_value, + kton_bond, }); Self::deposit_event(RawEvent::Proposed(c)); @@ -243,10 +243,10 @@ decl_module! { pub struct Proposal { proposer: AccountId, beneficiary: AccountId, - ring_bond: RingBalance, ring_value: RingBalance, - kton_bond: KtonBalance, + ring_bond: RingBalance, kton_value: KtonBalance, + kton_bond: KtonBalance, } decl_storage! { @@ -325,10 +325,18 @@ impl Module { /// The needed bond for a proposal whose spend is `value`. fn calculate_bonds(ring: RingBalance, kton: KtonBalance) -> (RingBalance, KtonBalance) { - ( - T::RingProposalBondMinimum::get().max(T::ProposalBond::get() * ring), - T::KtonProposalBondMinimum::get().max(T::ProposalBond::get() * kton), - ) + let mut ring_bond: RingBalance = RingBalance::::from(0); + let mut kton_bond: KtonBalance = KtonBalance::::from(0); + + if ring > ring_bond { + ring_bond = T::RingProposalBondMinimum::get().max(T::ProposalBond::get() * ring); + } + + if kton > kton_bond { + kton_bond = T::KtonProposalBondMinimum::get().max(T::ProposalBond::get() * kton); + } + + (ring_bond, kton_bond) } // Spend some money! @@ -344,49 +352,41 @@ impl Module { Approvals::mutate(|v| { v.retain(|&index| { - // Should always be true, but shouldn't panic if false or we're screwed. + // Should always be some, but shouldn't panic if false or we're screwed. + let mut should_return = false; let option_proposal = Self::proposals(index); if option_proposal.is_none() { return false; } let p = option_proposal.unwrap(); - if p.ring_value > budget_remaining_ring || p.kton_value > budget_remaining_kton { - if p.ring_value > budget_remaining_ring { - should_burn_ring = false; - } + if p.ring_value > budget_remaining_ring || p.ring_value == RingBalance::::from(0) { + should_burn_ring = false; + should_return = true; + } else { + budget_remaining_ring -= p.ring_value; + let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); + imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); + } - if p.kton_value > budget_remaining_kton { - should_burn_kton = false; + if p.kton_value > budget_remaining_kton || p.kton_value == KtonBalance::::from(0) { + should_burn_kton = false; + if should_return { + return true; } - - return true; + } else { + budget_remaining_kton -= p.kton_value; + let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); + imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); } - budget_remaining_ring -= p.ring_value; - budget_remaining_kton -= p.kton_value; >::remove(index); - - // return their deposit. - let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); - let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); - - // provide the allocation. - imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); - imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); - Self::deposit_event(RawEvent::Awarded(index, p.ring_value, p.kton_value, p.beneficiary)); false }); }); // burn balances - if should_burn_kton { - let burn = (T::Burn::get() * budget_remaining_kton).min(budget_remaining_kton); - budget_remaining_kton -= burn; - imbalance_kton.subsume(T::KtonCurrency::burn(burn)); - } - if should_burn_ring { // burn some proportion of the remaining budget if we run a surplus. let burn = (T::Burn::get() * budget_remaining_ring).min(budget_remaining_ring); @@ -394,13 +394,19 @@ impl Module { imbalance_ring.subsume(T::RingCurrency::burn(burn)); } + if should_burn_kton { + let burn = (T::Burn::get() * budget_remaining_kton).min(budget_remaining_kton); + budget_remaining_kton -= burn; + imbalance_kton.subsume(T::KtonCurrency::burn(burn)); + } + // Must never be an error, but better to be safe. // proof: budget_remaining is account free balance minus ED; // Thus we can't spend more than account free balance minus ED; // Thus account is kept alive; qed; - if let Err(problem) = T::KtonCurrency::settle( + if let Err(problem) = T::RingCurrency::settle( &Self::account_id(), - imbalance_kton, + imbalance_ring, WithdrawReason::Transfer.into(), ExistenceRequirement::KeepAlive, ) { @@ -409,9 +415,9 @@ impl Module { drop(problem); } - if let Err(problem) = T::RingCurrency::settle( + if let Err(problem) = T::KtonCurrency::settle( &Self::account_id(), - imbalance_ring, + imbalance_kton, WithdrawReason::Transfer.into(), ExistenceRequirement::KeepAlive, ) { diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 2cf585dba..d91a62158 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -145,8 +145,8 @@ fn spend_proposal_takes_proportional_deposit() { new_test_ext().execute_with(|| { assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); assert_eq!(Ring::free_balance(&0), 95); - assert_eq!(Ring::reserved_balance(&0), 5); assert_eq!(Kton::free_balance(&0), 95); + assert_eq!(Ring::reserved_balance(&0), 5); assert_eq!(Kton::reserved_balance(&0), 5); }); } @@ -154,7 +154,6 @@ fn spend_proposal_takes_proportional_deposit() { #[test] fn spend_proposal_fails_when_proposer_poor() { new_test_ext().execute_with(|| { - // ring assert_noop!( Treasury::propose_spend(Origin::signed(2), 100, 100, 3), Error::::InsufficientProposersBalance, @@ -162,6 +161,26 @@ fn spend_proposal_fails_when_proposer_poor() { }); } +#[test] +fn no_spend_no_burn() { + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 0, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 100); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 100); + }); +} + #[test] fn accepted_spend_proposal_ignored_outside_spend_period() { new_test_ext().execute_with(|| { From 80581e5fc0ec33e9a35b878c117e84ebf52e3e6f Mon Sep 17 00:00:00 2001 From: clearloop Date: Mon, 2 Mar 2020 23:34:15 +0800 Subject: [PATCH 15/19] add: more tests to no_spent_no_burn --- frame/treasury/src/tests.rs | 105 ++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 21 deletions(-) diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index d91a62158..004dbc535 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -128,7 +128,6 @@ fn minting_works() { }); } -/// min deposit is 0 now #[test] fn spend_proposal_takes_min_deposit() { new_test_ext().execute_with(|| { @@ -161,26 +160,6 @@ fn spend_proposal_fails_when_proposer_poor() { }); } -#[test] -fn no_spend_no_burn() { - new_test_ext().execute_with(|| { - Ring::make_free_balance_be(&Treasury::account_id(), 101); - Kton::make_free_balance_be(&Treasury::account_id(), 101); - - assert_eq!(Treasury::pot::(), 100); - assert_eq!(Treasury::pot::(), 100); - - assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 0, 3)); - assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - - >::on_finalize(2); - assert_eq!(Ring::free_balance(&3), 100); - assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 0); - assert_eq!(Treasury::pot::(), 100); - }); -} - #[test] fn accepted_spend_proposal_ignored_outside_spend_period() { new_test_ext().execute_with(|| { @@ -419,3 +398,87 @@ fn inexisting_account_works() { assert_eq!(Kton::free_balance(&3), 99); // Balance of `3` has changed }); } + +#[test] +fn no_spent_no_burn() { + // ring + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 0, 100, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Kton::free_balance(&3), 100); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 0); + }); + + // kton + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 0, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 100); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 100); + }); + + // both + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 0, 0, 3)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + }); +} + +/// FIXME: If this is true? +#[test] +fn no_accept_no_reject_keep_burning() { + new_test_ext().execute_with(|| { + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); + // assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); + // assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + + >::on_finalize(2); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 50); + assert_eq!(Treasury::pot::(), 50); + + >::on_finalize(4); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 25); + assert_eq!(Treasury::pot::(), 25); + }); +} From d235280ed03c57f283c4a83ba4995f694081744a Mon Sep 17 00:00:00 2001 From: clearloop Date: Tue, 3 Mar 2020 08:04:13 +0800 Subject: [PATCH 16/19] add: common logic tests for treasury --- frame/treasury/src/tests.rs | 241 ++++++++++++++++++++++++++++++++++-- 1 file changed, 232 insertions(+), 9 deletions(-) diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 004dbc535..23f0d49bb 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -456,29 +456,252 @@ fn no_spent_no_burn() { }); } -/// FIXME: If this is true? +/// # Logic Tests. +/// +/// **FIXME**: If some logic went wrong. +/// +/// + Proposal: A suggestion to allocate funds from the pot to a beneficiary. +/// + Beneficiary: An account who will receive the funds from a proposal iff the proposal is approved. +/// + Deposit: Funds that a proposer must lock when making a proposal. +/// The deposit will be returned or slashed if the proposal is approved or rejected respectively. +/// + Pot: Unspent funds accumulated by the treasury module. #[test] -fn no_accept_no_reject_keep_burning() { +fn approve_proposal_no_keep_burning() { new_test_ext().execute_with(|| { + // backtrace init configs. + assert_eq!(Ring::free_balance(&0), 100); + assert_eq!(Kton::free_balance(&0), 100); + assert_eq!(Ring::free_balance(&1), 98); + assert_eq!(Kton::free_balance(&1), 98); + assert_eq!(Ring::free_balance(&2), 1); + assert_eq!(Kton::free_balance(&2), 1); + assert_eq!(Ring::free_balance(&3), 0); + assert_eq!(Kton::free_balance(&3), 0); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + + // Ensure an account's free balance equals some value; this will create the account if needed. + // Returns a signed imbalance and status to indicate if the account was successfully updated + // or update has led to killing of the account. Ring::make_free_balance_be(&Treasury::account_id(), 101); Kton::make_free_balance_be(&Treasury::account_id(), 101); assert_eq!(Treasury::pot::(), 100); assert_eq!(Treasury::pot::(), 100); + // Put forward a suggestion for spending, burn treasury balances to AccontID-3 assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); - // assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); - // assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); + assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0)); // Accept proposal - >::on_finalize(2); + // @0-1: Check balances after `propose_spend` + >::on_finalize(1); + assert_eq!(Ring::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Kton::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + + // @2: On the first spend perid + >::on_finalize(2); // SpendPeriod: u64 = 2; + assert_eq!(Ring::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **No burn if approve** + assert_eq!(Kton::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **No burn if approve** + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 100); // No changes + assert_eq!(Kton::free_balance(&3), 100); // No changes + assert_eq!(Treasury::pot::(), 0); // Burn: Permill::from_percent(50); **Burn 100 if approve** + assert_eq!(Treasury::pot::(), 0); // Burn: Permill::from_percent(50); **Burn 100 if approve** + + // @3: Check balances on the perid after spend perid + >::on_finalize(3); + assert_eq!(Ring::free_balance(&0), 100); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 100); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 100); // No changes + assert_eq!(Kton::free_balance(&3), 100); // No changes + assert_eq!(Treasury::pot::(), 0); // No changes from last perid + assert_eq!(Treasury::pot::(), 0); // No changes from last perid + + // @4: The second spend perid + >::on_finalize(4); + assert_eq!(Ring::free_balance(&0), 100); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 100); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 100); // No changes + assert_eq!(Kton::free_balance(&3), 100); // No changes + assert_eq!(Treasury::pot::(), 0); // No changes from last perid + assert_eq!(Treasury::pot::(), 0); // No changes from last perid + }); +} + +#[test] +fn reject_proposal_keep_burning() { + new_test_ext().execute_with(|| { + // backtrace init configs. + assert_eq!(Ring::free_balance(&0), 100); + assert_eq!(Kton::free_balance(&0), 100); + assert_eq!(Ring::free_balance(&1), 98); + assert_eq!(Kton::free_balance(&1), 98); + assert_eq!(Ring::free_balance(&2), 1); + assert_eq!(Kton::free_balance(&2), 1); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 50); - assert_eq!(Treasury::pot::(), 50); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + + // Ensure an account's free balance equals some value; this will create the account if needed. + // Returns a signed imbalance and status to indicate if the account was successfully updated + // or update has led to killing of the account. + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + // Put forward a suggestion for spending, burn treasury balances to AccontID-3 + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); + // @0-1: Check balances after `propose_spend` + >::on_finalize(1); + assert_eq!(Ring::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Kton::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + + // @2: On the first spend perid + >::on_finalize(2); // SpendPeriod: u64 = 2; + assert_eq!(Ring::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Kton::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 50); // Burn: Permill::from_percent(50); **The Burned Balances just burned?** + assert_eq!(Treasury::pot::(), 50); // Burn: Permill::from_percent(50); **The Burned Balances just burned?** + + // @3: Check balances on the perid after spend perid + >::on_finalize(3); + assert_eq!(Ring::free_balance(&0), 95); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 95); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 50); // No changes from last perid + assert_eq!(Treasury::pot::(), 50); // No changes from last perid + + // @4: The second spend perid >::on_finalize(4); + assert_eq!(Ring::free_balance(&0), 95); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 95); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 25); // No changes from last perid + assert_eq!(Treasury::pot::(), 25); // No changes from last perid + }); +} + +#[test] +fn no_accept_no_reject_keep_burning() { + new_test_ext().execute_with(|| { + // backtrace init configs. + assert_eq!(Ring::free_balance(&0), 100); + assert_eq!(Kton::free_balance(&0), 100); + assert_eq!(Ring::free_balance(&1), 98); + assert_eq!(Kton::free_balance(&1), 98); + assert_eq!(Ring::free_balance(&2), 1); + assert_eq!(Kton::free_balance(&2), 1); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 25); - assert_eq!(Treasury::pot::(), 25); + assert_eq!(Treasury::pot::(), 0); + assert_eq!(Treasury::pot::(), 0); + + // Ensure an account's free balance equals some value; this will create the account if needed. + // Returns a signed imbalance and status to indicate if the account was successfully updated + // or update has led to killing of the account. + Ring::make_free_balance_be(&Treasury::account_id(), 101); + Kton::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 100); + + // Put forward a suggestion for spending, burn treasury balances to AccontID-3 + assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); + + // @0-1: Check balances after `propose_spend` + >::on_finalize(1); + assert_eq!(Ring::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Kton::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + assert_eq!(Treasury::pot::(), 100); // No changes + + // @2: On the first spend perid + >::on_finalize(2); // SpendPeriod: u64 = 2; + assert_eq!(Ring::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Kton::free_balance(&0), 95); // ProposalBond: Permill::from_percent(5); + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 50); // Burn: Permill::from_percent(50); **The Burned Balances just burned?** + assert_eq!(Treasury::pot::(), 50); // Burn: Permill::from_percent(50); **The Burned Balances just burned?** + + // @3: Check balances on the perid after spend perid + >::on_finalize(3); + assert_eq!(Ring::free_balance(&0), 95); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 95); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 50); // No changes from last perid + assert_eq!(Treasury::pot::(), 50); // No changes from last perid + + // @4: The second spend perid + >::on_finalize(4); + assert_eq!(Ring::free_balance(&0), 95); // No changes from last perid + assert_eq!(Kton::free_balance(&0), 95); // No changes from last perid + assert_eq!(Ring::free_balance(&1), 98); // No changes + assert_eq!(Kton::free_balance(&1), 98); // No changes + assert_eq!(Ring::free_balance(&2), 1); // No changes + assert_eq!(Kton::free_balance(&2), 1); // No changes + assert_eq!(Ring::free_balance(&3), 0); // No changes + assert_eq!(Kton::free_balance(&3), 0); // No changes + assert_eq!(Treasury::pot::(), 25); // No changes from last perid + assert_eq!(Treasury::pot::(), 25); // No changes from last perid }); } From c08ca16ed99395f91915f098e5ebcff84950188c Mon Sep 17 00:00:00 2001 From: clearloop Date: Tue, 3 Mar 2020 14:38:56 +0800 Subject: [PATCH 17/19] fix: approve should failed since one of kton or ring is not satisfied --- bin/node/runtime/src/lib.rs | 7 ++++--- frame/treasury/src/lib.rs | 42 ++++++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index fdda83446..128f44603 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -490,7 +490,8 @@ impl pallet_staking::Trait for Runtime { parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); - pub const ProposalBondMinimum: Balance = 1 * COIN; + pub const RingProposalBondMinimum: Balance = 1 * COIN; + pub const KtonProposalBondMinimum: Balance = 1 * COIN; pub const SpendPeriod: BlockNumber = 1 * DAYS; pub const Burn: Permill = Permill::from_percent(50); } @@ -504,8 +505,8 @@ impl pallet_treasury::Trait for Runtime { type KtonProposalRejection = (); type RingProposalRejection = (); type ProposalBond = ProposalBond; - type RingProposalBondMinimum = (); - type KtonProposalBondMinimum = (); + type RingProposalBondMinimum = RingProposalBondMinimum; + type KtonProposalBondMinimum = KtonProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; } diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 1c323b0cd..4988bad6c 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -345,36 +345,48 @@ impl Module { let mut budget_remaining_kton = Self::pot::(); let mut imbalance_ring = >::zero(); let mut imbalance_kton = >::zero(); - let mut should_burn_ring = true; - let mut should_burn_kton = true; + let mut miss_any_ring = false; + let mut miss_any_kton = false; Self::deposit_event(RawEvent::Spending(budget_remaining_ring, budget_remaining_kton)); Approvals::mutate(|v| { v.retain(|&index| { // Should always be some, but shouldn't panic if false or we're screwed. - let mut should_return = false; let option_proposal = Self::proposals(index); if option_proposal.is_none() { return false; } let p = option_proposal.unwrap(); - if p.ring_value > budget_remaining_ring || p.ring_value == RingBalance::::from(0) { - should_burn_ring = false; - should_return = true; - } else { + if p.ring_value > budget_remaining_ring { + if p.ring_value > budget_remaining_ring { + miss_any_ring = true; + } + + if p.kton_value > budget_remaining_kton { + miss_any_kton = true; + } + + return true; + } + + // no spent no burn + if p.ring_value == RingBalance::::from(0) { + miss_any_ring = true; + } + + if p.kton_value == KtonBalance::::from(0) { + miss_any_kton = true; + } + + if p.ring_value > RingBalance::::from(0) { budget_remaining_ring -= p.ring_value; let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); } - if p.kton_value > budget_remaining_kton || p.kton_value == KtonBalance::::from(0) { - should_burn_kton = false; - if should_return { - return true; - } - } else { + if p.kton_value > KtonBalance::::from(0) { budget_remaining_kton -= p.kton_value; let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); @@ -387,14 +399,14 @@ impl Module { }); // burn balances - if should_burn_ring { + if !miss_any_ring { // burn some proportion of the remaining budget if we run a surplus. let burn = (T::Burn::get() * budget_remaining_ring).min(budget_remaining_ring); budget_remaining_ring -= burn; imbalance_ring.subsume(T::RingCurrency::burn(burn)); } - if should_burn_kton { + if !miss_any_kton { let burn = (T::Burn::get() * budget_remaining_kton).min(budget_remaining_kton); budget_remaining_kton -= burn; imbalance_kton.subsume(T::KtonCurrency::burn(burn)); From e2950b9c43ab974e3f6120558312582ea10c5e47 Mon Sep 17 00:00:00 2001 From: clearloop Date: Tue, 3 Mar 2020 15:26:24 +0800 Subject: [PATCH 18/19] optimize: merge if-else closures --- frame/treasury/src/lib.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 4988bad6c..184405100 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -371,25 +371,20 @@ impl Module { return true; } - // no spent no burn - if p.ring_value == RingBalance::::from(0) { - miss_any_ring = true; - } - - if p.kton_value == KtonBalance::::from(0) { - miss_any_kton = true; - } - if p.ring_value > RingBalance::::from(0) { budget_remaining_ring -= p.ring_value; let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); + } else { + miss_any_ring = true; } if p.kton_value > KtonBalance::::from(0) { budget_remaining_kton -= p.kton_value; let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); + } else { + miss_any_kton = true; } >::remove(index); From 463a47b5c75fdf48e91728ef498f1aee86f5f887 Mon Sep 17 00:00:00 2001 From: clearloop Date: Tue, 3 Mar 2020 18:50:53 +0800 Subject: [PATCH 19/19] fix: conditon mixed up --- frame/treasury/src/lib.rs | 10 +++------- frame/treasury/src/tests.rs | 13 +++++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index 184405100..1456c3c77 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -359,7 +359,7 @@ impl Module { } let p = option_proposal.unwrap(); - if p.ring_value > budget_remaining_ring { + if p.ring_value > budget_remaining_ring || p.kton_value > budget_remaining_kton { if p.ring_value > budget_remaining_ring { miss_any_ring = true; } @@ -371,20 +371,16 @@ impl Module { return true; } - if p.ring_value > RingBalance::::from(0) { + if p.ring_value <= budget_remaining_ring { budget_remaining_ring -= p.ring_value; let _ = T::RingCurrency::unreserve(&p.proposer, p.ring_bond); imbalance_ring.subsume(T::RingCurrency::deposit_creating(&p.beneficiary, p.ring_value)); - } else { - miss_any_ring = true; } - if p.kton_value > KtonBalance::::from(0) { + if p.kton_value <= budget_remaining_kton { budget_remaining_kton -= p.kton_value; let _ = T::KtonCurrency::unreserve(&p.proposer, p.kton_bond); imbalance_kton.subsume(T::KtonCurrency::deposit_creating(&p.beneficiary, p.kton_value)); - } else { - miss_any_kton = true; } >::remove(index); diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 23f0d49bb..2f08a446f 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -415,7 +415,7 @@ fn no_spent_no_burn() { >::on_finalize(2); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 100); - assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 50); assert_eq!(Treasury::pot::(), 0); }); @@ -434,7 +434,7 @@ fn no_spent_no_burn() { assert_eq!(Ring::free_balance(&3), 100); assert_eq!(Kton::free_balance(&3), 0); assert_eq!(Treasury::pot::(), 0); - assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 50); }); // both @@ -451,8 +451,8 @@ fn no_spent_no_burn() { >::on_finalize(2); assert_eq!(Ring::free_balance(&3), 0); assert_eq!(Kton::free_balance(&3), 0); - assert_eq!(Treasury::pot::(), 100); - assert_eq!(Treasury::pot::(), 100); + assert_eq!(Treasury::pot::(), 50); + assert_eq!(Treasury::pot::(), 50); }); } @@ -507,8 +507,8 @@ fn approve_proposal_no_keep_burning() { // @2: On the first spend perid >::on_finalize(2); // SpendPeriod: u64 = 2; - assert_eq!(Ring::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **No burn if approve** - assert_eq!(Kton::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **No burn if approve** + assert_eq!(Ring::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **return bond** + assert_eq!(Kton::free_balance(&0), 100); // ProposalBond: Permill::from_percent(5); **return bond** assert_eq!(Ring::free_balance(&1), 98); // No changes assert_eq!(Kton::free_balance(&1), 98); // No changes assert_eq!(Ring::free_balance(&2), 1); // No changes @@ -571,6 +571,7 @@ fn reject_proposal_keep_burning() { // Put forward a suggestion for spending, burn treasury balances to AccontID-3 assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 100, 3)); + assert_ok!(Treasury::reject_proposal(Origin::ROOT, 0)); // @0-1: Check balances after `propose_spend` >::on_finalize(1);