Generalised proxies (#6156)

* Initial work

* It should work

* Fix node

* Fix tests

* Initial test

* Tests

* Expunge proxy functionality from democracy and elections

* Allow different proxy types

* Repotted

* Build

* Build

* Making a start on weights

* Undo breaking change

* Line widths.

* Fix

* fix tests

* finish benchmarks?

* Storage name!

* Utility -> Proxy

* proxy weight

* add proxy weight

* remove weights

* Update transfer constraint

* Again, fix constraints

* Fix negation

* Update frame/proxy/Cargo.toml

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Remove unneeded event.

* Grumbles

* Apply suggestions from code review

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2020-06-02 18:15:15 +02:00
committed by GitHub
parent ffea161765
commit 4adac40c07
20 changed files with 724 additions and 738 deletions
@@ -97,16 +97,6 @@ fn account_vote<T: Trait>(b: BalanceOf<T>) -> AccountVote<BalanceOf<T>> {
}
}
fn open_activate_proxy<T: Trait>(u: u32) -> Result<(T::AccountId, T::AccountId), &'static str> {
let caller = funded_account::<T>("caller", u);
let voter = funded_account::<T>("voter", u);
Democracy::<T>::open_proxy(RawOrigin::Signed(caller.clone()).into(), voter.clone())?;
Democracy::<T>::activate_proxy(RawOrigin::Signed(voter.clone()).into(), caller.clone())?;
Ok((caller, voter))
}
benchmarks! {
_ { }
@@ -215,70 +205,6 @@ benchmarks! {
assert_eq!(tally.nays, 1000.into(), "changed vote was not recorded");
}
// Basically copy paste of `vote_new`
proxy_vote_new {
let r in 1 .. MAX_REFERENDUMS;
let (caller, voter) = open_activate_proxy::<T>(0)?;
let account_vote = account_vote::<T>(100.into());
// Populate existing direct votes for the voter, they can vote on their own behalf
for i in 0 .. r {
let ref_idx = add_referendum::<T>(i)?;
Democracy::<T>::vote(RawOrigin::Signed(voter.clone()).into(), ref_idx, account_vote.clone())?;
}
let referendum_index = add_referendum::<T>(r)?;
}: proxy_vote(RawOrigin::Signed(caller), referendum_index, account_vote)
verify {
let votes = match VotingOf::<T>::get(&voter) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), (r + 1) as usize, "Vote was not recorded.");
}
// Basically copy paste of `vote_existing`
proxy_vote_existing {
let r in 1 .. MAX_REFERENDUMS;
let (caller, voter) = open_activate_proxy::<T>(0)?;
let account_vote = account_vote::<T>(100.into());
// We need to create existing direct votes
for i in 0 ..=r {
let ref_idx = add_referendum::<T>(i)?;
Democracy::<T>::vote(RawOrigin::Signed(voter.clone()).into(), ref_idx, account_vote.clone())?;
}
let votes = match VotingOf::<T>::get(&voter) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), (r + 1) as usize, "Votes were not recorded.");
// Change vote from aye to nay
let nay = Vote { aye: false, conviction: Conviction::Locked1x };
let new_vote = AccountVote::Standard { vote: nay, balance: 1000.into() };
let referendum_index = Democracy::<T>::referendum_count() - 1;
// This tests when a user changes a vote
}: proxy_vote(RawOrigin::Signed(caller.clone()), referendum_index, new_vote)
verify {
let votes = match VotingOf::<T>::get(&voter) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), (r + 1) as usize, "Vote was incorrectly added");
let referendum_info = Democracy::<T>::referendum_info(referendum_index)
.ok_or("referendum doesn't exist")?;
let tally = match referendum_info {
ReferendumInfo::Ongoing(r) => r.tally,
_ => return Err("referendum not ongoing"),
};
assert_eq!(tally.nays, 1000.into(), "changed vote was not recorded");
}
emergency_cancel {
let r in 1 .. MAX_REFERENDUMS;
let origin = T::CancellationOrigin::successful_origin();
@@ -505,33 +431,6 @@ benchmarks! {
}
}
activate_proxy {
let u in 1 .. MAX_USERS;
let caller: T::AccountId = funded_account::<T>("caller", u);
let proxy: T::AccountId = funded_account::<T>("proxy", u);
Democracy::<T>::open_proxy(RawOrigin::Signed(proxy.clone()).into(), caller.clone())?;
}: _(RawOrigin::Signed(caller.clone()), proxy.clone())
verify {
assert_eq!(Democracy::<T>::proxy(proxy), Some(ProxyState::Active(caller)));
}
close_proxy {
let u in 1 .. MAX_USERS;
let (caller, _) = open_activate_proxy::<T>(u)?;
}: _(RawOrigin::Signed(caller.clone()))
verify {
assert_eq!(Democracy::<T>::proxy(caller), None);
}
deactivate_proxy {
let u in 1 .. MAX_USERS;
let (caller, voter) = open_activate_proxy::<T>(u)?;
}: _(RawOrigin::Signed(voter.clone()), caller.clone())
verify {
assert_eq!(Democracy::<T>::proxy(caller), Some(ProxyState::Open(voter)));
}
delegate {
let r in 1 .. MAX_REFERENDUMS;
@@ -760,14 +659,6 @@ benchmarks! {
assert_eq!(voting.locked_balance(), base_balance);
}
open_proxy {
let u in 1 .. MAX_USERS;
let caller: T::AccountId = funded_account::<T>("caller", u);
let proxy: T::AccountId = funded_account::<T>("proxy", u);
}: _(RawOrigin::Signed(proxy), caller)
remove_vote {
let r in 1 .. MAX_REFERENDUMS;
@@ -831,131 +722,6 @@ benchmarks! {
assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
}
// This is a copy of delegate benchmark, but with `open_activate_proxy`
proxy_delegate {
let r in 1 .. MAX_REFERENDUMS;
let initial_balance: BalanceOf<T> = 100.into();
let delegated_balance: BalanceOf<T> = 1000.into();
let (caller, voter) = open_activate_proxy::<T>(0)?;
// Voter will initially delegate to `old_delegate`
let old_delegate: T::AccountId = funded_account::<T>("old_delegate", r);
Democracy::<T>::delegate(
RawOrigin::Signed(voter.clone()).into(),
old_delegate.clone(),
Conviction::Locked1x,
delegated_balance,
)?;
let (target, balance) = match VotingOf::<T>::get(&voter) {
Voting::Delegating { target, balance, .. } => (target, balance),
_ => return Err("Votes are not direct"),
};
assert_eq!(target, old_delegate, "delegation target didn't work");
assert_eq!(balance, delegated_balance, "delegation balance didn't work");
// Voter will now switch to `new_delegate`
let new_delegate: T::AccountId = funded_account::<T>("new_delegate", r);
let account_vote = account_vote::<T>(initial_balance);
// We need to create existing direct votes for the `new_delegate`
for i in 0..r {
let ref_idx = add_referendum::<T>(i)?;
Democracy::<T>::vote(RawOrigin::Signed(new_delegate.clone()).into(), ref_idx, account_vote.clone())?;
}
let votes = match VotingOf::<T>::get(&new_delegate) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
}: _(RawOrigin::Signed(caller.clone()), new_delegate.clone(), Conviction::Locked1x, delegated_balance)
verify {
let (target, balance) = match VotingOf::<T>::get(&voter) {
Voting::Delegating { target, balance, .. } => (target, balance),
_ => return Err("Votes are not direct"),
};
assert_eq!(target, new_delegate, "delegation target didn't work");
assert_eq!(balance, delegated_balance, "delegation balance didn't work");
let delegations = match VotingOf::<T>::get(&new_delegate) {
Voting::Direct { delegations, .. } => delegations,
_ => return Err("Votes are not direct"),
};
assert_eq!(delegations.capital, delegated_balance, "delegation was not recorded.");
}
// This is a copy of undelegate benchmark, but with `open_activate_proxy`
proxy_undelegate {
let r in 1 .. MAX_REFERENDUMS;
let initial_balance: BalanceOf<T> = 100.into();
let delegated_balance: BalanceOf<T> = 1000.into();
let (caller, voter) = open_activate_proxy::<T>(0)?;
// Caller will delegate
let the_delegate: T::AccountId = funded_account::<T>("delegate", r);
Democracy::<T>::delegate(
RawOrigin::Signed(voter.clone()).into(),
the_delegate.clone(),
Conviction::Locked1x,
delegated_balance,
)?;
let (target, balance) = match VotingOf::<T>::get(&voter) {
Voting::Delegating { target, balance, .. } => (target, balance),
_ => return Err("Votes are not direct"),
};
assert_eq!(target, the_delegate, "delegation target didn't work");
assert_eq!(balance, delegated_balance, "delegation balance didn't work");
// We need to create votes direct votes for the `delegate`
let account_vote = account_vote::<T>(initial_balance);
for i in 0..r {
let ref_idx = add_referendum::<T>(i)?;
Democracy::<T>::vote(
RawOrigin::Signed(the_delegate.clone()).into(),
ref_idx,
account_vote.clone()
)?;
}
let votes = match VotingOf::<T>::get(&the_delegate) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), r as usize, "Votes were not recorded.");
}: _(RawOrigin::Signed(caller.clone()))
verify {
// Voting should now be direct
match VotingOf::<T>::get(&voter) {
Voting::Direct { .. } => (),
_ => return Err("undelegation failed"),
}
}
proxy_remove_vote {
let r in 1 .. MAX_REFERENDUMS;
let (caller, voter) = open_activate_proxy::<T>(0)?;
let account_vote = account_vote::<T>(100.into());
for i in 0 .. r {
let ref_idx = add_referendum::<T>(i)?;
Democracy::<T>::vote(RawOrigin::Signed(voter.clone()).into(), ref_idx, account_vote.clone())?;
}
let votes = match VotingOf::<T>::get(&voter) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), r as usize, "Votes not created");
let referendum_index = r - 1;
}: _(RawOrigin::Signed(caller.clone()), referendum_index)
verify {
let votes = match VotingOf::<T>::get(&voter) {
Voting::Direct { votes, .. } => votes,
_ => return Err("Votes are not direct"),
};
assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed");
}
enact_proposal_execute {
// Num of bytes in encoded proposal
let b in 0 .. MAX_BYTES;
@@ -1012,8 +778,6 @@ mod tests {
assert_ok!(test_benchmark_second::<Test>());
assert_ok!(test_benchmark_vote_new::<Test>());
assert_ok!(test_benchmark_vote_existing::<Test>());
assert_ok!(test_benchmark_proxy_vote_new::<Test>());
assert_ok!(test_benchmark_proxy_vote_existing::<Test>());
assert_ok!(test_benchmark_emergency_cancel::<Test>());
assert_ok!(test_benchmark_external_propose::<Test>());
assert_ok!(test_benchmark_external_propose_majority::<Test>());
@@ -1025,10 +789,6 @@ mod tests {
assert_ok!(test_benchmark_on_initialize_external::<Test>());
assert_ok!(test_benchmark_on_initialize_public::<Test>());
assert_ok!(test_benchmark_on_initialize_no_launch_no_maturing::<Test>());
assert_ok!(test_benchmark_open_proxy::<Test>());
assert_ok!(test_benchmark_activate_proxy::<Test>());
assert_ok!(test_benchmark_close_proxy::<Test>());
assert_ok!(test_benchmark_deactivate_proxy::<Test>());
assert_ok!(test_benchmark_delegate::<Test>());
assert_ok!(test_benchmark_undelegate::<Test>());
assert_ok!(test_benchmark_clear_public_proposals::<Test>());
@@ -1039,9 +799,6 @@ mod tests {
assert_ok!(test_benchmark_unlock_set::<Test>());
assert_ok!(test_benchmark_remove_vote::<Test>());
assert_ok!(test_benchmark_remove_other_vote::<Test>());
assert_ok!(test_benchmark_proxy_delegate::<Test>());
assert_ok!(test_benchmark_proxy_undelegate::<Test>());
assert_ok!(test_benchmark_proxy_remove_vote::<Test>());
assert_ok!(test_benchmark_enact_proposal_execute::<Test>());
assert_ok!(test_benchmark_enact_proposal_slash::<Test>());
});
+1 -289
View File
@@ -52,8 +52,6 @@
//! account or an external origin) suggests that the system adopt.
//! - **Referendum:** A proposal that is in the process of being voted on for
//! either acceptance or rejection as a change to the system.
//! - **Proxy:** An account that has full voting power on behalf of a separate "Stash" account
//! that holds the funds.
//! - **Delegation:** The act of granting your voting power to the decisions of another account for
//! up to a certain conviction.
//!
@@ -93,18 +91,6 @@
//! - `reap_vote` - Remove some account's expired votes.
//! - `unlock` - Redetermine the account's balance lock, potentially making tokens available.
//!
//! Proxy administration:
//! - `activate_proxy` - Activates a proxy that is already open to the sender.
//! - `close_proxy` - Clears the proxy status, called by the proxy.
//! - `deactivate_proxy` - Deactivates a proxy back to the open status, called by the stash.
//! - `open_proxy` - Opens a proxy account on behalf of the sender.
//!
//! Proxy actions:
//! - `proxy_vote` - Votes in a referendum on behalf of a stash account.
//! - `proxy_unvote` - Cancel a previous vote, done on behalf of the voter by a proxy.
//! - `proxy_delegate` - Delegate voting power, done on behalf of the voter by a proxy.
//! - `proxy_undelegate` - Stop delegating voting power, done on behalf of the voter by a proxy.
//!
//! Preimage actions:
//! - `note_preimage` - Registers the preimage for an upcoming proposal, requires
//! a deposit that is returned once the proposal is enacted.
@@ -191,7 +177,7 @@ mod types;
pub use vote_threshold::{Approved, VoteThreshold};
pub use vote::{Vote, AccountVote, Voting};
pub use conviction::Conviction;
pub use types::{ReferendumInfo, ReferendumStatus, ProxyState, Tally, UnvoteScope, Delegations};
pub use types::{ReferendumInfo, ReferendumStatus, Tally, UnvoteScope, Delegations};
#[cfg(test)]
mod tests;
@@ -376,14 +362,6 @@ decl_storage! {
/// TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway.
pub VotingOf: map hasher(twox_64_concat) T::AccountId => Voting<BalanceOf<T>, T::AccountId, T::BlockNumber>;
/// Who is able to vote for whom. Value is the fund-holding account, key is the
/// vote-transaction-sending account.
///
/// TWOX-NOTE: OK ― `AccountId` is a secure hash.
// TODO: Refactor proxy into its own pallet.
// https://github.com/paritytech/substrate/issues/5322
pub Proxy get(fn proxy): map hasher(twox_64_concat) T::AccountId => Option<ProxyState<T::AccountId>>;
/// Accounts for which there are locks in action which may be removed at some point in the
/// future. The value is the block number at which the lock expires and may be removed.
///
@@ -467,8 +445,6 @@ decl_error! {
ValueLow,
/// Proposal does not exist
ProposalMissing,
/// Not a proxy
NotProxy,
/// Unknown index
BadIndex,
/// Cannot cancel the same proposal twice
@@ -485,10 +461,6 @@ decl_error! {
NoProposal,
/// Identity may not veto a proposal twice
AlreadyVetoed,
/// Already a proxy
AlreadyProxy,
/// Wrong proxy
WrongProxy,
/// Not delegated
NotDelegated,
/// Preimage already noted
@@ -511,12 +483,6 @@ 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,
/// The given account did not vote on the referendum.
NotVoter,
/// The actor has no permission to conduct the action.
@@ -575,27 +541,6 @@ mod weight_for {
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `proxy_delegate`.
/// same as `delegate with additional:
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `proxy account`
/// - Base Weight: 68.61 + 8.039 * R µs
pub fn proxy_delegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(5), votes.saturating_add(4))
.saturating_add(69_000_000)
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `proxy_undelegate`.
/// same as `undelegate with additional:
/// Db reads: `Proxy`
/// Base Weight: 39 + 7.958 * R µs
pub fn proxy_undelegate<T: Trait>(votes: Weight) -> Weight {
T::DbWeight::get().reads_writes(votes.saturating_add(3), votes.saturating_add(2))
.saturating_add(40_000_000)
.saturating_add(votes.saturating_mul(8_000_000))
}
/// Calculate the weight for `note_preimage`.
/// # <weight>
/// - Complexity: `O(E)` with E size of `encoded_proposal` (protected by a required deposit).
@@ -766,34 +711,6 @@ decl_module! {
Self::try_vote(&who, ref_index, vote)
}
/// Vote in a referendum on behalf of a stash. If `vote.is_aye()`, the vote is to enact
/// the proposal; otherwise it is a vote to keep the status quo.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `ref_index`: The index of the referendum to proxy vote for.
/// - `vote`: The vote configuration.
///
/// # <weight>
/// - Complexity: `O(R)` where R is the number of referendums the proxy has voted on.
/// weight is charged as if maximum votes.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`, `balances locks`, `Proxy`, `proxy account`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`, `balances locks`
/// ------------
/// - Base Weight:
/// - Proxy Vote New: 54.35 + .344 * R µs
/// - Proxy Vote Existing: 54.35 + .35 * R µs
/// # </weight>
#[weight = 55_000_000 + 350_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(5, 3)]
fn proxy_vote(origin,
#[compact] ref_index: ReferendumIndex,
vote: AccountVote<BalanceOf<T>>,
) -> DispatchResult {
let who = ensure_signed(origin)?;
let voter = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
Self::try_vote(&voter, ref_index, vote)
}
/// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same
/// referendum.
///
@@ -1028,86 +945,6 @@ decl_module! {
})
}
/// Specify a proxy that is already open to us. Called by the stash.
///
/// NOTE: Used to be called `set_proxy`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `proxy`: The account that will be activated as proxy.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`
/// - Db writes: `Proxy`
/// - Base Weight: 7.972 µs
/// # </weight>
#[weight = 8_000_000 + T::DbWeight::get().reads_writes(1, 1)]
fn activate_proxy(origin, proxy: T::AccountId) {
let who = ensure_signed(origin)?;
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.
///
/// NOTE: Used to be called `resign_proxy`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`, `sender account`
/// - Db writes: `Proxy`, `sender account`
/// - Base Weight: 15.41 µs
/// # </weight>
#[weight = 16_000_000 + T::DbWeight::get().reads_writes(1, 1)]
fn close_proxy(origin) {
let who = ensure_signed(origin)?;
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`.
///
/// The dispatch origin of this call must be _Signed_.
///
/// - `proxy`: The account that will be deactivated as proxy.
///
/// # <weight>
/// - Complexity: `O(1)`
/// - Db reads: `Proxy`
/// - Db writes: `Proxy`
/// - Base Weight: 8.03 µs
/// # </weight>
#[weight = 8_000_000 + T::DbWeight::get().reads_writes(1, 1)]
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 the voting power (with some given conviction) of the sending account.
///
/// The balance delegated is locked for as long as it's delegated, and thereafter for the
@@ -1314,33 +1151,6 @@ decl_module! {
Self::update_lock(&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>
/// - Complexity: O(1)
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `Proxy`, `proxy account`
/// - Base Weight: 14.86 µs
/// # </weight>
#[weight = 15_000_000 + T::DbWeight::get().reads_writes(2, 2)]
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));
});
}
/// Remove a vote for a referendum.
///
/// If:
@@ -1407,94 +1217,6 @@ decl_module! {
Ok(())
}
/// Delegate the voting power (with some given conviction) of a proxied account.
///
/// The balance delegated is locked for as long as it's delegated, and thereafter for the
/// time appropriate for the conviction's lock period.
///
/// The dispatch origin of this call must be _Signed_, and the signing account must have
/// been set as the proxy account for `target`.
///
/// - `target`: The account whole voting power shall be delegated and whose balance locked.
/// This account must either:
/// - be delegating already; or
/// - have no voting activity (if there is, then it will need to be removed/consolidated
/// through `reap_vote` or `unvote`).
/// - `to`: The account whose voting the `target` account's voting power will follow.
/// - `conviction`: The conviction that will be attached to the delegated votes. When the
/// account is undelegated, the funds will be locked for the corresponding period.
/// - `balance`: The amount of the account's balance to be used in delegating. This must
/// not be more than the account's current balance.
///
/// Emits `Delegated`.
///
/// # <weight>
/// same as `delegate with additional:
/// - Db reads: `Proxy`, `proxy account`
/// - Db writes: `proxy account`
/// - Base Weight: 68.61 + 8.039 * R µs
/// # </weight>
#[weight = weight_for::proxy_delegate::<T>(T::MaxVotes::get().into())]
pub fn proxy_delegate(origin,
to: T::AccountId,
conviction: Conviction,
balance: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
let votes = Self::try_delegate(target, to, conviction, balance)?;
Ok(Some(weight_for::proxy_delegate::<T>(votes.into())).into())
}
/// Undelegate the voting power of a proxied account.
///
/// Tokens may be unlocked following once an amount of time consistent with the lock period
/// of the conviction with which the delegation was issued.
///
/// The dispatch origin of this call must be _Signed_ and the signing account must be a
/// proxy for some other account which is currently delegating.
///
/// Emits `Undelegated`.
///
/// # <weight>
/// same as `undelegate with additional:
/// Db reads: `Proxy`
/// Base Weight: 39 + 7.958 * R µs
/// # </weight>
#[weight = weight_for::proxy_undelegate::<T>(T::MaxVotes::get().into())]
fn proxy_undelegate(origin) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
let votes = Self::try_undelegate(target)?;
Ok(Some(weight_for::proxy_undelegate::<T>(votes.into())).into())
}
/// Remove a proxied vote for a referendum.
///
/// Exactly equivalent to `remove_vote` except that it operates on the account that the
/// sender is a proxy for.
///
/// The dispatch origin of this call must be _Signed_ and the signing account must be a
/// proxy for some other account which has a registered vote for the referendum of `index`.
///
/// - `index`: The index of referendum of the vote to be removed.
///
/// # <weight>
/// - `O(R + log R)` where R is the number of referenda that `target` has voted on.
/// Weight is calculated for the maximum number of vote.
/// - Db reads: `ReferendumInfoOf`, `VotingOf`, `Proxy`
/// - Db writes: `ReferendumInfoOf`, `VotingOf`
/// - Base Weight: 26.35 + .36 * R µs
/// # </weight>
#[weight = 26_000_000 + 360_000 * Weight::from(T::MaxVotes::get()) + T::DbWeight::get().reads_writes(2, 3)]
fn proxy_remove_vote(origin, index: ReferendumIndex) -> DispatchResult {
let who = ensure_signed(origin)?;
let target = Self::proxy(who).and_then(|a| a.as_active()).ok_or(Error::<T>::NotProxy)?;
Self::try_remove_vote(&target, index, UnvoteScope::Any)
}
/// Enact a proposal from a referendum. For now we just make the weight be the maximum.
#[weight = T::MaximumBlockWeight::get()]
fn enact_proposal(origin, proposal_hash: T::Hash, index: ReferendumIndex) -> DispatchResult {
@@ -1538,16 +1260,6 @@ impl<T: Trait> Module<T> {
// Exposed mutables.
#[cfg(feature = "std")]
pub fn force_proxy(stash: T::AccountId, proxy: T::AccountId) {
Proxy::<T>::mutate(&proxy, |o| {
if o.is_none() {
system::Module::<T>::inc_ref(&proxy);
}
*o = Some(ProxyState::Active(stash))
})
}
/// Start a referendum.
pub fn internal_start_referendum(
proposal_hash: T::Hash,
-1
View File
@@ -38,7 +38,6 @@ mod external_proposing;
mod fast_tracking;
mod lock_voting;
mod preimage;
mod proxying;
mod public_proposals;
mod scheduling;
mod voting;
@@ -1,105 +0,0 @@
// This file is part of Substrate.
// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! The tests for functionality concerning proxying.
use super::*;
#[test]
fn proxy_should_work() {
new_test_ext().execute_with(|| {
assert_eq!(Democracy::proxy(10), None);
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::activate_proxy(Origin::signed(2), 10), Error::<Test>::AlreadyProxy);
// But this works because 11 isn't proxying.
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::deactivate_proxy(Origin::signed(2), 10), Error::<Test>::WrongProxy);
// 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));
// 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));
});
}
#[test]
fn voting_and_removing_votes_should_work_with_proxy() {
new_test_ext().execute_with(|| {
System::set_block_number(0);
assert_ok!(propose_set_balance_and_note(1, 2, 1));
fast_forward_to(2);
let r = 0;
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(1)));
assert_eq!(tally(r), Tally { ayes: 1, nays: 0, turnout: 10 });
assert_ok!(Democracy::proxy_remove_vote(Origin::signed(10), r));
assert_eq!(tally(r), Tally { ayes: 0, nays: 0, turnout: 0 });
});
}
#[test]
fn delegation_and_undelegation_should_work_with_proxy() {
new_test_ext().execute_with(|| {
System::set_block_number(0);
assert_ok!(propose_set_balance_and_note(1, 2, 1));
fast_forward_to(2);
let r = 0;
assert_ok!(Democracy::open_proxy(Origin::signed(10), 1));
assert_ok!(Democracy::activate_proxy(Origin::signed(1), 10));
assert_ok!(Democracy::vote(Origin::signed(2), r, aye(2)));
assert_ok!(Democracy::proxy_delegate(Origin::signed(10), 2, Conviction::None, 10));
assert_eq!(tally(r), Tally { ayes: 3, nays: 0, turnout: 30 });
assert_ok!(Democracy::proxy_undelegate(Origin::signed(10)));
assert_eq!(tally(r), Tally { ayes: 2, nays: 0, turnout: 20 });
});
}
-18
View File
@@ -197,24 +197,6 @@ impl<BlockNumber, Hash, Balance: Default> ReferendumInfo<BlockNumber, Hash, Bala
}
}
/// 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> {
pub (crate) fn as_active(self) -> Option<AccountId> {
match self {
ProxyState::Active(a) => Some(a),
ProxyState::Open(_) => None,
}
}
}
/// Whether an `unvote` operation is able to make actions that are not strictly always in the
/// interest of an account.
pub enum UnvoteScope {