Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ construct_runtime!(
Recovery: pallet_recovery::{Module, Call, Storage, Event<T>},
Vesting: pallet_vesting::{Module, Call, Storage, Event<T>, Config<T>},
Scheduler: pallet_scheduler::{Module, Call, Storage, Event<T>},
Proxy: pallet_proxy::{Module, Call, Storage, Event},
Proxy: pallet_proxy::{Module, Call, Storage, Event<T>},
}
);

Expand Down
1 change: 0 additions & 1 deletion frame/democracy/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use sp_runtime::traits::{Bounded, One};
use crate::Module as Democracy;

const SEED: u32 = 0;
const MAX_USERS: u32 = 1000;
const MAX_REFERENDUMS: u32 = 100;
const MAX_PROPOSALS: u32 = 100;
const MAX_SECONDERS: u32 = 100;
Expand Down
5 changes: 3 additions & 2 deletions frame/proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ codec = { package = "parity-scale-codec", version = "1.3.0", default-features =
frame-support = { version = "2.0.0-rc2", default-features = false, path = "../support" }
frame-system = { version = "2.0.0-rc2", default-features = false, path = "../system" }
sp-core = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/core" }
sp-io = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/std" }

Expand All @@ -25,7 +26,6 @@ frame-benchmarking = { version = "2.0.0-rc2", default-features = false, path = "
[dev-dependencies]
sp-core = { version = "2.0.0-rc2", path = "../../primitives/core" }
pallet-balances = { version = "2.0.0-rc2", path = "../balances" }
sp-io = { version = "2.0.0-rc2", path = "../../primitives/io" }

[features]
default = ["std"]
Expand All @@ -35,7 +35,8 @@ std = [
"sp-runtime/std",
"frame-support/std",
"frame-system/std",
"sp-std/std"
"sp-std/std",
"sp-io/std"
]
runtime-benchmarks = [
"frame-benchmarking",
Expand Down
26 changes: 23 additions & 3 deletions frame/proxy/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use crate::Module as Proxy;

const SEED: u32 = 0;

fn add_proxies<T: Trait>(n: u32) -> Result<(), &'static str> {
let caller: T::AccountId = account("caller", 0, SEED);
fn add_proxies<T: Trait>(n: u32, maybe_who: Option<T::AccountId>) -> Result<(), &'static str> {
let caller = maybe_who.unwrap_or_else(|| account("caller", 0, SEED));
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
for i in 0..n {
Proxy::<T>::add_proxy(
Expand All @@ -42,7 +42,7 @@ fn add_proxies<T: Trait>(n: u32) -> Result<(), &'static str> {

benchmarks! {
_ {
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p)?;
let p in 1 .. (T::MaxProxies::get() - 1).into() => add_proxies::<T>(p, None)?;
}

proxy {
Expand All @@ -68,6 +68,24 @@ benchmarks! {
let p in ...;
let caller: T::AccountId = account("caller", 0, SEED);
}: _(RawOrigin::Signed(caller))

anonymous {
let p in ...;
}: _(RawOrigin::Signed(account("caller", 0, SEED)), T::ProxyType::default(), 0)

kill_anonymous {
let p in 0 .. (T::MaxProxies::get() - 2).into();

let caller: T::AccountId = account("caller", 0, SEED);
T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
Module::<T>::anonymous(RawOrigin::Signed(account("caller", 0, SEED)).into(), T::ProxyType::default(), 0)?;
let height = system::Module::<T>::block_number();
let ext_index = system::Module::<T>::extrinsic_index().unwrap_or(0);
let anon = Module::<T>::anonymous_account(&caller, &T::ProxyType::default(), 0, None);

add_proxies::<T>(p, Some(anon.clone()))?;

}: _(RawOrigin::Signed(anon), caller, T::ProxyType::default(), 0, height, ext_index)
}

#[cfg(test)]
Expand All @@ -83,6 +101,8 @@ mod tests {
assert_ok!(test_benchmark_add_proxy::<Test>());
assert_ok!(test_benchmark_remove_proxy::<Test>());
assert_ok!(test_benchmark_remove_proxies::<Test>());
assert_ok!(test_benchmark_anonymous::<Test>());
assert_ok!(test_benchmark_kill_anonymous::<Test>());
});
}
}
134 changes: 121 additions & 13 deletions frame/proxy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@
#![cfg_attr(not(feature = "std"), no_std)]

use sp_std::prelude::*;
use frame_support::{decl_module, decl_event, decl_error, decl_storage, Parameter, ensure};
use codec::{Encode, Decode};
use sp_io::hashing::blake2_256;
use sp_runtime::{DispatchResult, traits::{Dispatchable, Zero}};
use sp_runtime::traits::Member;
use frame_support::{
decl_module, decl_event, decl_error, decl_storage, Parameter, ensure,
traits::{Get, ReservableCurrency, Currency, Filter, InstanceFilter},
weights::{GetDispatchInfo, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}},
dispatch::{PostDispatchInfo, IsSubType},
};
use frame_system::{self as system, ensure_signed};
use sp_runtime::{DispatchResult, traits::{Dispatchable, Zero}};
use sp_runtime::traits::Member;

mod tests;
mod benchmarking;
Expand All @@ -53,7 +55,7 @@ type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trai
/// Configuration trait.
pub trait Trait: frame_system::Trait {
/// The overarching event type.
type Event: From<Event> + Into<<Self as frame_system::Trait>::Event>;
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;

/// The overarching call type.
type Call: Parameter + Dispatchable<Origin=Self::Origin, PostInfo=PostDispatchInfo>
Expand Down Expand Up @@ -115,9 +117,15 @@ decl_error! {

decl_event! {
/// Events type.
pub enum Event {
pub enum Event<T> where
AccountId = <T as frame_system::Trait>::AccountId,
ProxyType = <T as Trait>::ProxyType
{
/// A proxy was executed correctly, with the given result.
ProxyExecuted(DispatchResult),
/// Anonymous account (first parameter) has been created by new proxy (second) with given
/// disambiguation index and proxy type.
AnonymousCreated(AccountId, AccountId, ProxyType, u16),
}
}

Expand Down Expand Up @@ -164,12 +172,12 @@ decl_module! {
.ok_or(Error::<T>::NotProxy)?;
match call.is_sub_type() {
Some(Call::add_proxy(_, ref pt)) | Some(Call::remove_proxy(_, ref pt)) =>
ensure!(&proxy_type == pt, Error::<T>::NoPermission),
ensure!(pt.is_no_more_permissive(&proxy_type), Error::<T>::NoPermission),
_ => (),
}
ensure!(proxy_type.filter(&call), Error::<T>::Unproxyable);
let e = call.dispatch(frame_system::RawOrigin::Signed(real).into());
Self::deposit_event(Event::ProxyExecuted(e.map(|_| ()).map_err(|e| e.error)));
Self::deposit_event(RawEvent::ProxyExecuted(e.map(|_| ()).map_err(|e| e.error)));
}

/// Register a proxy account for the sender that is able to make calls on its behalf.
Expand All @@ -186,8 +194,8 @@ decl_module! {
/// - DB weight: 1 storage read and write.
/// # </weight>
#[weight = T::DbWeight::get().reads_writes(1, 1)
.saturating_add(18 * WEIGHT_PER_MICROS)
.saturating_add((200 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
.saturating_add(18 * WEIGHT_PER_MICROS)
.saturating_add((200 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
]
fn add_proxy(origin, proxy: T::AccountId, proxy_type: T::ProxyType) -> DispatchResult {
let who = ensure_signed(origin)?;
Expand Down Expand Up @@ -222,8 +230,8 @@ decl_module! {
/// - DB weight: 1 storage read and write.
/// # </weight>
#[weight = T::DbWeight::get().reads_writes(1, 1)
.saturating_add(14 * WEIGHT_PER_MICROS)
.saturating_add((160 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
.saturating_add(14 * WEIGHT_PER_MICROS)
.saturating_add((160 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
]
fn remove_proxy(origin, proxy: T::AccountId, proxy_type: T::ProxyType) -> DispatchResult {
let who = ensure_signed(origin)?;
Expand Down Expand Up @@ -253,19 +261,119 @@ decl_module! {
///
/// The dispatch origin for this call must be _Signed_.
///
/// WARNING: This may be called on accounts created by `anonymous`, however if done, then
/// the unreserved fees will be inaccessible. **All access to this account will be lost.**
///
/// # <weight>
/// P is the number of proxies the user has
/// - Base weight: 13.73 + .129 * P µs
/// - DB weight: 1 storage read and write.
/// # </weight>
#[weight = T::DbWeight::get().reads_writes(1, 1)
.saturating_add(14 * WEIGHT_PER_MICROS)
.saturating_add((130 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
.saturating_add(14 * WEIGHT_PER_MICROS)
.saturating_add((130 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
]
fn remove_proxies(origin) {
let who = ensure_signed(origin)?;
let (_, old_deposit) = Proxies::<T>::take(&who);
T::Currency::unreserve(&who, old_deposit);
}

/// Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and
/// initialize it with a proxy of `proxy_type` for `origin` sender.
///
/// Requires a `Signed` origin.
///
/// - `proxy_type`: The type of the proxy that the sender will be registered as over the
/// new account. This will almost always be the most permissive `ProxyType` possible to
/// allow for maximum flexibility.
/// - `index`: A disambiguation index, in case this is called multiple times in the same
/// transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just
/// want to use `0`.
///
/// Fails with `Duplicate` if this has already been called in this transaction, from the
/// same sender, with the same parameters.
///
/// Fails if there are insufficient funds to pay for deposit.
///
/// # <weight>
/// P is the number of proxies the user has
/// - Base weight: 36.48 + .039 * P µs
/// - DB weight: 1 storage read and write.
/// # </weight>
#[weight = T::DbWeight::get().reads_writes(1, 1)
.saturating_add(36 * WEIGHT_PER_MICROS)
.saturating_add((40 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
]
fn anonymous(origin, proxy_type: T::ProxyType, index: u16) {
let who = ensure_signed(origin)?;

let anonymous = Self::anonymous_account(&who, &proxy_type, index, None);
ensure!(!Proxies::<T>::contains_key(&anonymous), Error::<T>::Duplicate);
let deposit = T::ProxyDepositBase::get() + T::ProxyDepositFactor::get();
T::Currency::reserve(&who, deposit)?;
Proxies::<T>::insert(&anonymous, (vec![(who.clone(), proxy_type.clone())], deposit));
Self::deposit_event(RawEvent::AnonymousCreated(anonymous, who, proxy_type, index));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if anonymous created also included height/ext_index which are important for killing the account, but not super easy to find

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ext_index is placed in the EventRecord. If you know the event then you'll also likely know the blocknumber as that's how they're indexed.

}

/// Removes a previously spawned anonymous proxy.
///
/// WARNING: **All access to this account will be lost.** Any funds held in it will be
/// inaccessible.
///
/// Requires a `Signed` origin, and the sender account must have been created by a call to
/// `anonymous` with corresponding parameters.
///
/// - `spawner`: The account that originally called `anonymous` to create this account.
/// - `index`: The disambiguation index originally passed to `anonymous`. Probably `0`.
/// - `proxy_type`: The proxy type originally passed to `anonymous`.
/// - `height`: The height of the chain when the call to `anonymous` was processed.
/// - `ext_index`: The extrinsic index in which the call to `anonymous` was processed.
///
/// Fails with `NoPermission` in case the caller is not a previously created anonymous
/// account whose `anonymous` call has corresponding parameters.
///
/// # <weight>
/// P is the number of proxies the user has
/// - Base weight: 15.65 + .137 * P µs
/// - DB weight: 1 storage read and write.
/// # </weight>
#[weight = T::DbWeight::get().reads_writes(1, 1)
.saturating_add(15 * WEIGHT_PER_MICROS)
.saturating_add((140 * WEIGHT_PER_NANOS).saturating_mul(T::MaxProxies::get().into()))
]
fn kill_anonymous(origin,
spawner: T::AccountId,
proxy_type: T::ProxyType,
index: u16,
#[compact] height: T::BlockNumber,
#[compact] ext_index: u32,
) {
let who = ensure_signed(origin)?;

let when = (height, ext_index);
let proxy = Self::anonymous_account(&spawner, &proxy_type, index, Some(when));
ensure!(proxy == who, Error::<T>::NoPermission);

let (_, deposit) = Proxies::<T>::take(&who);
T::Currency::unreserve(&spawner, deposit);
}
}
}

impl<T: Trait> Module<T> {
pub fn anonymous_account(
who: &T::AccountId,
proxy_type: &T::ProxyType,
index: u16,
maybe_when: Option<(T::BlockNumber, u32)>,
) -> T::AccountId {
let (height, ext_index) = maybe_when.unwrap_or_else(|| (
system::Module::<T>::block_number(),
system::Module::<T>::extrinsic_index().unwrap_or_default()
));
let entropy = (b"modlpy/proxy____", who, height, ext_index, proxy_type, index)
.using_encoded(blake2_256);
T::AccountId::decode(&mut &entropy[..]).unwrap_or_default()
}
}
Loading