Lazy reaping (#4895)

* Squash and rebase from gav-lazy-reaping

* Bump version

* Bump runtime again

* Docs.

* Remove old functions

* Update frame/balances/src/lib.rs

Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update frame/contracts/src/lib.rs

Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com>

* Warnings

* Bump runtime version

* Update frame/democracy/src/lib.rs

Co-Authored-By: Shawn Tabrizi <shawntabrizi@gmail.com>

* Update frame/system/src/lib.rs

* Clean up OnReapAccount

* Use frame_support debug

* Bump spec

* Renames and fix

* Fix

* Fix rename

* Fix

* Increase time for test

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Benjamin Kampmann <ben.kampmann@googlemail.com>
This commit is contained in:
Gavin Wood
2020-02-24 14:04:42 -03:00
committed by GitHub
parent c412c6230e
commit afa5861f3b
62 changed files with 604 additions and 287 deletions
+134 -46
View File
@@ -30,7 +30,7 @@ use frame_support::{
weights::SimpleDispatchInfo,
traits::{
Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get,
OnReapAccount, OnUnbalanced, BalanceStatus
OnUnbalanced, BalanceStatus
}
};
use frame_system::{self as system, ensure_signed, ensure_root};
@@ -260,6 +260,24 @@ impl<BlockNumber: Parameter, Hash: Parameter> ReferendumInfo<BlockNumber, Hash>
}
}
/// State of a proxy voting account.
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug)]
pub enum ProxyState<AccountId> {
/// Account is open to becoming a proxy but is not yet assigned.
Open(AccountId),
/// Account is actively being a proxy.
Active(AccountId),
}
impl<AccountId> ProxyState<AccountId> {
fn as_active(self) -> Option<AccountId> {
match self {
ProxyState::Active(a) => Some(a),
ProxyState::Open(_) => None,
}
}
}
decl_storage! {
trait Store for Module<T: Trait> as Democracy {
/// The number of (public) proposals that have been made so far.
@@ -299,7 +317,7 @@ decl_storage! {
/// Who is able to vote for whom. Value is the fund-holding account, key is the
/// vote-transaction-sending account.
pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option<T::AccountId>;
pub Proxy get(fn proxy): map hasher(blake2_256) T::AccountId => Option<ProxyState<T::AccountId>>;
/// Get the account (and lock periods) to which another account is delegating vote.
pub Delegations get(fn delegations):
@@ -423,6 +441,12 @@ decl_error! {
NotLocked,
/// The lock on the account to be unlocked has not yet expired.
NotExpired,
/// A proxy-pairing was attempted to an account that was not open.
NotOpen,
/// A proxy-pairing was attempted to an account that was open to another account.
WrongOpen,
/// A proxy-de-pairing was attempted to an account that was not active.
NotActive
}
}
@@ -525,8 +549,9 @@ decl_module! {
#[compact] ref_index: ReferendumIndex,
vote: Vote
) -> DispatchResult {
let who = Self::proxy(ensure_signed(origin)?).ok_or(Error::<T>::NotProxy)?;
Self::do_vote(who, ref_index, vote)
let who = ensure_signed(origin)?;
let voter = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
Self::do_vote(voter, ref_index, vote)
}
/// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same
@@ -659,42 +684,65 @@ decl_module! {
}
}
/// Specify a proxy. Called by the stash.
/// Specify a proxy that is already open to us. Called by the stash.
///
/// NOTE: Used to be called `set_proxy`.
///
/// # <weight>
/// - One extra DB entry.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(100_000)]
fn set_proxy(origin, proxy: T::AccountId) {
fn activate_proxy(origin, proxy: T::AccountId) {
let who = ensure_signed(origin)?;
ensure!(!<Proxy<T>>::contains_key(&proxy), Error::<T>::AlreadyProxy);
<Proxy<T>>::insert(proxy, who)
Proxy::<T>::try_mutate(&proxy, |a| match a.take() {
None => Err(Error::<T>::NotOpen),
Some(ProxyState::Active(_)) => Err(Error::<T>::AlreadyProxy),
Some(ProxyState::Open(x)) if &x == &who => {
*a = Some(ProxyState::Active(who));
Ok(())
}
Some(ProxyState::Open(_)) => Err(Error::<T>::WrongOpen),
})?;
}
/// Clear the proxy. Called by the proxy.
///
/// # <weight>
/// - One DB clear.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(100_000)]
fn resign_proxy(origin) {
let who = ensure_signed(origin)?;
<Proxy<T>>::remove(who);
}
/// Clear the proxy. Called by the stash.
/// NOTE: Used to be called `resign_proxy`.
///
/// # <weight>
/// - One DB clear.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(100_000)]
fn remove_proxy(origin, proxy: T::AccountId) {
fn close_proxy(origin) {
let who = ensure_signed(origin)?;
ensure!(
&Self::proxy(&proxy).ok_or(Error::<T>::NotProxy)? == &who,
Error::<T>::WrongProxy,
);
<Proxy<T>>::remove(proxy);
Proxy::<T>::mutate(&who, |a| {
if a.is_some() {
system::Module::<T>::dec_ref(&who);
}
*a = None;
});
}
/// Deactivate the proxy, but leave open to this account. Called by the stash.
///
/// The proxy must already be active.
///
/// NOTE: Used to be called `remove_proxy`.
///
/// # <weight>
/// - One DB clear.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(100_000)]
fn deactivate_proxy(origin, proxy: T::AccountId) {
let who = ensure_signed(origin)?;
Proxy::<T>::try_mutate(&proxy, |a| match a.take() {
None | Some(ProxyState::Open(_)) => Err(Error::<T>::NotActive),
Some(ProxyState::Active(x)) if &x == &who => {
*a = Some(ProxyState::Open(who));
Ok(())
}
Some(ProxyState::Active(_)) => Err(Error::<T>::WrongProxy),
})?;
}
/// Delegate vote.
@@ -818,6 +866,30 @@ decl_module! {
Locks::<T>::remove(&target);
Self::deposit_event(RawEvent::Unlocked(target));
}
/// Become a proxy.
///
/// This must be called prior to a later `activate_proxy`.
///
/// Origin must be a Signed.
///
/// - `target`: The account whose votes will later be proxied.
///
/// `close_proxy` must be called before the account can be destroyed.
///
/// # <weight>
/// - One extra DB entry.
/// # </weight>
#[weight = SimpleDispatchInfo::FixedNormal(100_000)]
fn open_proxy(origin, target: T::AccountId) {
let who = ensure_signed(origin)?;
Proxy::<T>::mutate(&who, |a| {
if a.is_none() {
system::Module::<T>::inc_ref(&who);
}
*a = Some(ProxyState::Open(target));
});
}
}
}
@@ -935,7 +1007,12 @@ impl<T: Trait> Module<T> {
#[cfg(feature = "std")]
pub fn force_proxy(stash: T::AccountId, proxy: T::AccountId) {
<Proxy<T>>::insert(proxy, stash)
Proxy::<T>::mutate(&proxy, |o| {
if o.is_none() {
system::Module::<T>::inc_ref(&proxy);
}
*o = Some(ProxyState::Active(stash))
})
}
/// Start a referendum.
@@ -1162,12 +1239,6 @@ impl<T: Trait> Module<T> {
}
}
impl<T: Trait> OnReapAccount<T::AccountId> for Module<T> {
fn on_reap_account(who: &T::AccountId) {
<Proxy<T>>::remove(who)
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -1229,7 +1300,7 @@ mod tests {
type ModuleToIndex = ();
type AccountData = pallet_balances::AccountData<u64>;
type OnNewAccount = ();
type OnReapAccount = Balances;
type OnKilledAccount = ();
}
parameter_types! {
pub const ExistentialDeposit: u64 = 1;
@@ -1968,29 +2039,45 @@ mod tests {
fn proxy_should_work() {
new_test_ext().execute_with(|| {
assert_eq!(Democracy::proxy(10), None);
assert_ok!(Democracy::set_proxy(Origin::signed(1), 10));
assert_eq!(Democracy::proxy(10), Some(1));
assert!(System::allow_death(&10));
assert_noop!(Democracy::activate_proxy(Origin::signed(1), 10), Error::<Test>::NotOpen);
assert_ok!(Democracy::open_proxy(Origin::signed(10), 1));
assert!(!System::allow_death(&10));
assert_eq!(Democracy::proxy(10), Some(ProxyState::Open(1)));
assert_noop!(Democracy::activate_proxy(Origin::signed(2), 10), Error::<Test>::WrongOpen);
assert_ok!(Democracy::activate_proxy(Origin::signed(1), 10));
assert_eq!(Democracy::proxy(10), Some(ProxyState::Active(1)));
// Can't set when already set.
assert_noop!(Democracy::set_proxy(Origin::signed(2), 10), Error::<Test>::AlreadyProxy);
assert_noop!(Democracy::activate_proxy(Origin::signed(2), 10), Error::<Test>::AlreadyProxy);
// But this works because 11 isn't proxying.
assert_ok!(Democracy::set_proxy(Origin::signed(2), 11));
assert_eq!(Democracy::proxy(10), Some(1));
assert_eq!(Democracy::proxy(11), Some(2));
assert_ok!(Democracy::open_proxy(Origin::signed(11), 2));
assert_ok!(Democracy::activate_proxy(Origin::signed(2), 11));
assert_eq!(Democracy::proxy(10), Some(ProxyState::Active(1)));
assert_eq!(Democracy::proxy(11), Some(ProxyState::Active(2)));
// 2 cannot fire 1's proxy:
assert_noop!(Democracy::remove_proxy(Origin::signed(2), 10), Error::<Test>::WrongProxy);
assert_noop!(Democracy::deactivate_proxy(Origin::signed(2), 10), Error::<Test>::WrongProxy);
// 1 fires his proxy:
assert_ok!(Democracy::remove_proxy(Origin::signed(1), 10));
assert_eq!(Democracy::proxy(10), None);
assert_eq!(Democracy::proxy(11), Some(2));
// 1 deactivates their proxy:
assert_ok!(Democracy::deactivate_proxy(Origin::signed(1), 10));
assert_eq!(Democracy::proxy(10), Some(ProxyState::Open(1)));
// but the proxy account cannot be killed until the proxy is closed.
assert!(!System::allow_death(&10));
// 11 resigns:
assert_ok!(Democracy::resign_proxy(Origin::signed(11)));
// and then 10 closes it completely:
assert_ok!(Democracy::close_proxy(Origin::signed(10)));
assert_eq!(Democracy::proxy(10), None);
assert!(System::allow_death(&10));
// 11 just closes without 2's "permission".
assert_ok!(Democracy::close_proxy(Origin::signed(11)));
assert_eq!(Democracy::proxy(11), None);
assert!(System::allow_death(&11));
});
}
@@ -2002,7 +2089,8 @@ mod tests {
fast_forward_to(2);
let r = 0;
assert_ok!(Democracy::set_proxy(Origin::signed(1), 10));
assert_ok!(Democracy::open_proxy(Origin::signed(10), 1));
assert_ok!(Democracy::activate_proxy(Origin::signed(1), 10));
assert_ok!(Democracy::proxy_vote(Origin::signed(10), r, AYE));
assert_eq!(Democracy::voters_for(r), vec![1]);