Initial mechanics for 80:20 fee split (#2912)

* Initial mechanics for 80:20 fee split

Also:
- Introduce extra functions for Imbalance manipulation;
- Store treasury pot in an account, letting total issuance account for
  it.

* Fix some tests

* Fix some tests

* Minor cleanups

* Update parity-codec version (#2855)

* Update parity-codec version

* Update grandpa, rhododendron and trie-bench

* Use primitive-types from crates.io

* Bump impl version

* Fix trie-bench version

* Fix lock files

* Fix versions

* Update codec to 4.1

* merge fix

* Revert merge

* More reversions

* Remove accidental code

* Update locks

* Bump runtime

* Update locks

* Tweaks and label TODO

* Update srml/treasury/src/lib.rs

Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com>

* Update issue number

* Update core/sr-primitives/src/traits.rs

Co-Authored-By: Robert Habermeier <rphmeier@gmail.com>

* Fix wasm build

* Fix subkey build
This commit is contained in:
Gavin Wood
2019-07-02 11:41:03 +02:00
committed by GitHub
parent bfe5347724
commit 9c6ebfeccd
14 changed files with 389 additions and 45 deletions
+13 -2
View File
@@ -18,6 +18,8 @@
//!
//! This tracks the current author of the block and recent uncles.
#![cfg_attr(not(feature = "std"), no_std)]
use rstd::prelude::*;
use rstd::collections::btree_set::BTreeSet;
use srml_support::{decl_module, decl_storage, for_each_tuple, StorageValue};
@@ -102,6 +104,15 @@ pub trait FilterUncle<Header, Author> {
-> Result<(Option<Author>, Self::Accumulator), &'static str>;
}
impl<H, A> FilterUncle<H, A> for () {
type Accumulator = ();
fn filter_uncle(_: &H, acc: Self::Accumulator)
-> Result<(Option<A>, Self::Accumulator), &'static str>
{
Ok((None, acc))
}
}
/// A filter on uncles which verifies seals and does no additional checks.
/// This is well-suited to consensus modes such as PoW where the cost of
/// equivocating is high.
@@ -201,8 +212,8 @@ decl_module! {
fn on_finalize() {
// ensure we never go to trie with these values.
let _ = <Self as Store>::Author::take();
let _ = <Self as Store>::DidSetUncles::take();
<Self as Store>::Author::kill();
<Self as Store>::DidSetUncles::kill();
}
/// Provide a set of uncles.
+24 -2
View File
@@ -161,7 +161,7 @@ use srml_support::traits::{
use srml_support::dispatch::Result;
use primitives::traits::{
Zero, SimpleArithmetic, StaticLookup, Member, CheckedAdd, CheckedSub,
MaybeSerializeDebug, Saturating
MaybeSerializeDebug, Saturating, Bounded
};
use system::{IsDeadAccount, OnNewAccount, ensure_signed};
@@ -313,7 +313,9 @@ decl_storage! {
///
/// `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets
/// collapsed to zero if it ever becomes less than `ExistentialDeposit`.
pub FreeBalance get(free_balance) build(|config: &GenesisConfig<T, I>| config.balances.clone()): map T::AccountId => T::Balance;
pub FreeBalance get(free_balance)
build(|config: &GenesisConfig<T, I>| config.balances.clone()):
map T::AccountId => T::Balance;
/// The amount of the balance of a given account that is externally reserved; this can still get
/// slashed, but gets slashed last of all.
@@ -735,6 +737,26 @@ where
<FreeBalance<T, I>>::get(who)
}
fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance {
<TotalIssuance<T, I>>::mutate(|issued|
issued.checked_sub(&amount).unwrap_or_else(|| {
amount = *issued;
Zero::zero()
})
);
PositiveImbalance::new(amount)
}
fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance {
<TotalIssuance<T, I>>::mutate(|issued|
*issued = issued.checked_add(&amount).unwrap_or_else(|| {
amount = Self::Balance::max_value() - *issued;
Self::Balance::max_value()
})
);
NegativeImbalance::new(amount)
}
// # <weight>
// Despite iterating over a list of locks, they are limited by the number of
// lock IDs, which means the number of runtime modules that intend to use and create locks.
+1
View File
@@ -11,6 +11,7 @@ srml-metadata = { path = "../metadata", default-features = false }
sr-std = { path = "../../core/sr-std", default-features = false }
runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false }
sr-primitives = { path = "../../core/sr-primitives", default-features = false }
substrate-primitives = { path = "../../core/primitives", default-features = false }
inherents = { package = "substrate-inherents", path = "../../core/inherents", default-features = false }
srml-support-procedural = { path = "./procedural" }
paste = "0.1"
+104 -12
View File
@@ -18,10 +18,11 @@
//!
//! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module.
use crate::rstd::result;
use crate::rstd::{result, marker::PhantomData, ops::Div};
use crate::codec::{Codec, Encode, Decode};
use substrate_primitives::u32_trait::Value as U32;
use crate::runtime_primitives::traits::{
MaybeSerializeDebug, SimpleArithmetic
MaybeSerializeDebug, SimpleArithmetic, Saturating
};
use crate::runtime_primitives::ConsensusEngineId;
@@ -111,6 +112,14 @@ pub trait FindAuthor<Author> {
where I: 'a + IntoIterator<Item=(ConsensusEngineId, &'a [u8])>;
}
impl<A> FindAuthor<A> for () {
fn find_author<'a, I>(_: I) -> Option<A>
where I: 'a + IntoIterator<Item=(ConsensusEngineId, &'a [u8])>
{
None
}
}
/// A trait for verifying the seal of a header and returning the author.
pub trait VerifySeal<Header, Author> {
/// Verify a header and return the author, if any.
@@ -270,6 +279,34 @@ impl<
}
}
/// Split an unbalanced amount two ways between a common divisor.
pub struct SplitTwoWays<
Balance,
Imbalance,
Part1,
Target1,
Part2,
Target2,
>(PhantomData<(Balance, Imbalance, Part1, Target1, Part2, Target2)>);
impl<
Balance: From<u32> + Saturating + Div<Output=Balance>,
I: Imbalance<Balance>,
Part1: U32,
Target1: OnUnbalanced<I>,
Part2: U32,
Target2: OnUnbalanced<I>,
> OnUnbalanced<I> for SplitTwoWays<Balance, I, Part1, Target1, Part2, Target2>
{
fn on_unbalanced(amount: I) {
let total: u32 = Part1::VALUE + Part2::VALUE;
let amount1 = amount.peek().saturating_mul(Part1::VALUE.into()) / total.into();
let (imb1, imb2) = amount.split(amount1);
Target1::on_unbalanced(imb1);
Target2::on_unbalanced(imb2);
}
}
/// Abstraction over a fungible assets system.
pub trait Currency<AccountId> {
/// The balance of an account.
@@ -299,6 +336,21 @@ pub trait Currency<AccountId> {
/// `ExistentialDeposit`.
fn minimum_balance() -> Self::Balance;
/// Reduce the total issuance by `amount` and return the according imbalance. The imbalance will
/// typically be used to reduce an account by the same amount with e.g. `settle`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is burnt, for example
/// in the case of underflow.
fn burn(amount: Self::Balance) -> Self::PositiveImbalance;
/// Increase the total issuance by `amount` and return the according imbalance. The imbalance
/// will typically be used to increase an account by the same amount with e.g.
/// `resolve_into_existing` or `resolve_creating`.
///
/// This is infallible, but doesn't guarantee that the entire `amount` is issued, for example
/// in the case of overflow.
fn issue(amount: Self::Balance) -> Self::NegativeImbalance;
/// The 'free' balance of a given account.
///
/// This is the only balance that matters in terms of most operations on tokens. It alone
@@ -355,17 +407,18 @@ pub trait Currency<AccountId> {
value: Self::Balance
) -> result::Result<Self::PositiveImbalance, &'static str>;
/// Removes some free balance from `who` account for `reason` if possible. If `liveness` is `KeepAlive`,
/// then no less than `ExistentialDeposit` must be left remaining.
///
/// This checks any locks, vesting, and liquidity requirements. If the removal is not possible, then it
/// returns `Err`.
fn withdraw(
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
/// success.
fn resolve_into_existing(
who: &AccountId,
value: Self::Balance,
reason: WithdrawReason,
liveness: ExistenceRequirement,
) -> result::Result<Self::NegativeImbalance, &'static str>;
value: Self::NegativeImbalance,
) -> result::Result<(), Self::NegativeImbalance> {
let v = value.peek();
match Self::deposit_into_existing(who, v) {
Ok(opposite) => Ok(drop(value.offset(opposite))),
_ => Err(value),
}
}
/// Adds up to `value` to the free balance of `who`. If `who` doesn't exist, it is created.
///
@@ -375,6 +428,45 @@ pub trait Currency<AccountId> {
value: Self::Balance,
) -> Self::PositiveImbalance;
/// Similar to deposit_creating, only accepts a `NegativeImbalance` and returns nothing on
/// success.
fn resolve_creating(
who: &AccountId,
value: Self::NegativeImbalance,
) {
let v = value.peek();
drop(value.offset(Self::deposit_creating(who, v)));
}
/// Removes some free balance from `who` account for `reason` if possible. If `liveness` is
/// `KeepAlive`, then no less than `ExistentialDeposit` must be left remaining.
///
/// This checks any locks, vesting, and liquidity requirements. If the removal is not possible,
/// then it returns `Err`.
///
/// If the operation is successful, this will return `Ok` with a `NegativeImbalance` whose value
/// is `value`.
fn withdraw(
who: &AccountId,
value: Self::Balance,
reason: WithdrawReason,
liveness: ExistenceRequirement,
) -> result::Result<Self::NegativeImbalance, &'static str>;
/// Similar to withdraw, only accepts a `PositiveImbalance` and returns nothing on success.
fn settle(
who: &AccountId,
value: Self::PositiveImbalance,
reason: WithdrawReason,
liveness: ExistenceRequirement,
) -> result::Result<(), Self::PositiveImbalance> {
let v = value.peek();
match Self::withdraw(who, v, reason, liveness) {
Ok(opposite) => Ok(drop(value.offset(opposite))),
_ => Err(value),
}
}
/// Ensure an account's free balance equals some value; this will create the account
/// if needed.
///
+49 -24
View File
@@ -70,11 +70,14 @@
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use rstd::prelude::*;
use srml_support::{StorageValue, StorageMap, decl_module, decl_storage, decl_event, ensure};
use srml_support::traits::{Currency, ReservableCurrency, OnDilution, OnUnbalanced, Imbalance};
use runtime_primitives::{Permill,
traits::{Zero, EnsureOrigin, StaticLookup, Saturating, CheckedSub, CheckedMul}
use srml_support::{StorageValue, StorageMap, decl_module, decl_storage, decl_event, ensure, print};
use srml_support::traits::{
Currency, ReservableCurrency, OnDilution, OnUnbalanced, Imbalance, WithdrawReason,
ExistenceRequirement
};
use runtime_primitives::{Permill, ModuleId, traits::{
Zero, EnsureOrigin, StaticLookup, CheckedSub, CheckedMul, AccountIdConversion
}};
use parity_codec::{Encode, Decode};
use system::ensure_signed;
@@ -82,6 +85,8 @@ type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::Ac
type PositiveImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::PositiveImbalance;
type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
const MODULE_ID: ModuleId = ModuleId(*b"py/trsry");
pub trait Trait: system::Trait {
/// The staking balance.
type Currency: Currency<Self::AccountId> + ReservableCurrency<Self::AccountId>;
@@ -135,12 +140,6 @@ decl_module! {
Self::deposit_event(RawEvent::Proposed(c));
}
/// Set the balance of funds available to spend.
fn set_pot(#[compact] new_pot: BalanceOf<T>) {
// Put the new value into storage.
<Pot<T>>::put(new_pot);
}
/// (Re-)configure this module.
fn configure(
#[compact] proposal_bond: Permill,
@@ -224,9 +223,6 @@ decl_storage! {
// State...
/// Total funds available to this module for spending.
Pot get(pot): BalanceOf<T>;
/// Number of proposals that have been made.
ProposalCount get(proposal_count): ProposalIndex;
@@ -260,6 +256,14 @@ decl_event!(
impl<T: Trait> Module<T> {
// Add public immutables and private mutables.
/// The account ID of the treasury pot.
///
/// This actually does computation. If you need to keep using it, then make sure you cache the
/// value and only call this once.
pub fn account_id() -> T::AccountId {
MODULE_ID.into_account()
}
/// The needed bond for a proposal whose spend is `value`.
fn calculate_bond(value: BalanceOf<T>) -> BalanceOf<T> {
Self::proposal_bond_minimum().max(Self::proposal_bond() * value)
@@ -298,18 +302,36 @@ impl<T: Trait> Module<T> {
});
});
T::MintedForSpending::on_unbalanced(imbalance);
if !missed_any {
// burn some proportion of the remaining budget if we run a surplus.
let burn = (Self::burn() * budget_remaining).min(budget_remaining);
budget_remaining -= burn;
imbalance.subsume(T::Currency::burn(burn));
Self::deposit_event(RawEvent::Burnt(burn))
}
Self::deposit_event(RawEvent::Rollover(budget_remaining));
if let Err(problem) = T::Currency::settle(
&Self::account_id(),
imbalance,
WithdrawReason::Transfer,
ExistenceRequirement::KeepAlive
) {
print("Inconsistent state - couldn't settle imbalance for funds spent by treasury");
// Nothing else to do here.
drop(problem);
}
<Pot<T>>::put(budget_remaining);
Self::deposit_event(RawEvent::Rollover(budget_remaining));
}
fn pot() -> BalanceOf<T> {
T::Currency::free_balance(&Self::account_id())
}
}
impl<T: Trait> OnUnbalanced<NegativeImbalanceOf<T>> for Module<T> {
fn on_unbalanced(amount: NegativeImbalanceOf<T>) {
T::Currency::resolve_creating(&Self::account_id(), amount);
}
}
@@ -322,7 +344,7 @@ impl<T: Trait> OnDilution<BalanceOf<T>> for Module<T> {
if let Some(funding) = total_issuance.checked_sub(&portion) {
let funding = funding / portion;
if let Some(funding) = funding.checked_mul(&minted) {
<Pot<T>>::mutate(|x| *x = x.saturating_add(funding));
Self::on_unbalanced(T::Currency::issue(funding));
}
}
}
@@ -519,6 +541,7 @@ mod tests {
fn accepted_spend_proposal_enacted_on_spend_period() {
with_externalities(&mut new_test_ext(), || {
Treasury::on_dilution(100, 100);
assert_eq!(Treasury::pot(), 100);
assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3));
assert_ok!(Treasury::approve_proposal(Origin::ROOT, 0));
@@ -536,25 +559,27 @@ mod tests {
fn on_dilution_quantization_effects() {
with_externalities(&mut new_test_ext(), || {
// minted = 1% of total issuance for all cases
let _ = Treasury::set_pot(0);
assert_eq!(Balances::total_issuance(), 200);
Treasury::on_dilution(2, 66); // portion = 33% of total issuance
assert_eq!(Treasury::pot(), 4); // should increase by 4 (200 - 66) / 66 * 2
Balances::make_free_balance_be(&Treasury::account_id(), 0);
Treasury::on_dilution(2, 67); // portion = 33+eps% of total issuance
assert_eq!(Treasury::pot(), 6); // should increase by 2 (200 - 67) / 67 * 2
assert_eq!(Treasury::pot(), 2); // should increase by 2 (200 - 67) / 67 * 2
Balances::make_free_balance_be(&Treasury::account_id(), 0);
Treasury::on_dilution(2, 100); // portion = 50% of total issuance
assert_eq!(Treasury::pot(), 8); // should increase by 2 (200 - 100) / 100 * 2
assert_eq!(Treasury::pot(), 2); // should increase by 2 (200 - 100) / 100 * 2
Balances::make_free_balance_be(&Treasury::account_id(), 0);
// If any more than 50% of the network is staked (i.e. (2 * portion) > total_issuance)
// then the pot will not increase.
Treasury::on_dilution(2, 101); // portion = 50+eps% of total issuance
assert_eq!(Treasury::pot(), 8); // should increase by 0 (200 - 101) / 101 * 2
assert_eq!(Treasury::pot(), 0); // should increase by 0 (200 - 101) / 101 * 2
Treasury::on_dilution(2, 134); // portion = 67% of total issuance
assert_eq!(Treasury::pot(), 8); // should increase by 0 (200 - 134) / 134 * 2
assert_eq!(Treasury::pot(), 0); // should increase by 0 (200 - 134) / 134 * 2
});
}
@@ -572,7 +597,7 @@ mod tests {
Treasury::on_dilution(100, 100);
<Treasury as OnFinalize<u64>>::on_finalize(4);
assert_eq!(Balances::free_balance(&3), 150);
assert_eq!(Treasury::pot(), 25);
assert_eq!(Treasury::pot(), 75);
});
}
}