diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index 84f1a84badc90..b0be841bcda34 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -159,6 +159,10 @@ pub mod pallet { #[pallet::constant] type MaxPending: Get; + /// The maximum amount of characters a keyless vanity address can have. + #[pallet::constant] + type MaxVanity: Get; + /// The type of hash used for hashing the call. type CallHasher: Hash; @@ -267,7 +271,7 @@ pub mod pallet { /// /// The dispatch origin for this call must be _Signed_. /// - /// WARNING: This may be called on accounts created by `anonymous`, however if done, then + /// WARNING: This may be called on accounts created by `proxied_keyless`, however if done, then /// the unreserved fees will be inaccessible. **All access to this account will be lost.** /// /// # @@ -295,6 +299,8 @@ pub mod pallet { /// want to use `0`. /// - `delay`: The announcement period required of the initial proxy. Will generally be /// zero. + /// - `maybe_keyless`: An account to be used as the keyless proxied account instead of the + /// generated one, it will be evaluated to determine if it's indeed a keyless account. /// /// Fails with `Duplicate` if this has already been called in this transaction, from the /// same sender, with the same parameters. @@ -305,17 +311,23 @@ pub mod pallet { /// Weight is a function of the number of proxies the user has (P). /// # /// TODO: Might be over counting 1 read - #[pallet::weight(T::WeightInfo::anonymous(T::MaxProxies::get().into()))] - pub fn anonymous( + #[pallet::weight(T::WeightInfo::proxied_keyless(T::MaxProxies::get().into()))] + pub fn proxied_keyless( origin: OriginFor, proxy_type: T::ProxyType, delay: T::BlockNumber, index: u16, + maybe_keyless: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; - let anonymous = Self::anonymous_account(&who, &proxy_type, index, None); - ensure!(!Proxies::::contains_key(&anonymous), Error::::Duplicate); + let keyless = if let Some(account) = maybe_keyless { + ensure!(Self::is_vain_enough(&account), Error::::NotKeyless); + account + } else { + Self::keyless_account(&who, &proxy_type, index, None) + }; + ensure!(!Proxies::::contains_key(&keyless), Error::::Duplicate); let proxy_def = ProxyDefinition { delegate: who.clone(), proxy_type: proxy_type.clone(), delay }; @@ -325,9 +337,9 @@ pub mod pallet { let deposit = T::ProxyDepositBase::get() + T::ProxyDepositFactor::get(); T::Currency::reserve(&who, deposit)?; - Proxies::::insert(&anonymous, (bounded_proxies, deposit)); - Self::deposit_event(Event::AnonymousCreated { - anonymous, + Proxies::::insert(&keyless, (bounded_proxies, deposit)); + Self::deposit_event(Event::KeylessCreated { + keyless, who, proxy_type, disambiguation_index: index, @@ -336,28 +348,28 @@ pub mod pallet { Ok(()) } - /// Removes a previously spawned anonymous proxy. + /// Removes a previously spawned keyless account. /// /// 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. + /// `proxied_keyless` 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. + /// - `spawner`: The account that originally called `proxied_keyless` to create this account. + /// - `index`: The disambiguation index originally passed to `proxied_keyless`. Probably `0`. + /// - `proxy_type`: The proxy type originally passed to `proxied_keyless`. + /// - `height`: The height of the chain when the call to `proxied_keyless` was processed. + /// - `ext_index`: The extrinsic index in which the call to `proxied_keyless` was processed. /// - /// Fails with `NoPermission` in case the caller is not a previously created anonymous - /// account whose `anonymous` call has corresponding parameters. + /// Fails with `NoPermission` in case the caller is not a previously created keyless + /// account whose `proxied_keyless` call has corresponding parameters. /// /// # /// Weight is a function of the number of proxies the user has (P). /// # - #[pallet::weight(T::WeightInfo::kill_anonymous(T::MaxProxies::get().into()))] - pub fn kill_anonymous( + #[pallet::weight(T::WeightInfo::kill_keyless(T::MaxProxies::get().into()))] + pub fn kill_keyless( origin: OriginFor, spawner: T::AccountId, proxy_type: T::ProxyType, @@ -368,7 +380,7 @@ pub mod pallet { let who = ensure_signed(origin)?; let when = (height, ext_index); - let proxy = Self::anonymous_account(&spawner, &proxy_type, index, Some(when)); + let proxy = Self::keyless_account(&spawner, &proxy_type, index, Some(when)); ensure!(proxy == who, Error::::NoPermission); let (_, deposit) = Proxies::::take(&who); @@ -536,9 +548,9 @@ pub mod pallet { let call_hash = T::CallHasher::hash_of(&call); let now = system::Pallet::::block_number(); Self::edit_announcements(&delegate, |ann| { - ann.real != real || - ann.call_hash != call_hash || - now.saturating_sub(ann.height) < def.delay + ann.real != real + || ann.call_hash != call_hash + || now.saturating_sub(ann.height) < def.delay }) .map_err(|_| Error::::Unannounced)?; @@ -555,8 +567,8 @@ pub mod pallet { ProxyExecuted { result: DispatchResult }, /// Anonymous account has been created by new proxy with given /// disambiguation index and proxy type. - AnonymousCreated { - anonymous: T::AccountId, + KeylessCreated { + keyless: T::AccountId, who: T::AccountId, proxy_type: T::ProxyType, disambiguation_index: u16, @@ -601,6 +613,8 @@ pub mod pallet { Unannounced, /// Cannot add self as proxy. NoSelfProxy, + /// The provided keyless account is not vain enough. + NotKeyless, } /// The set of account proxies. Maps the account which has delegated to the accounts @@ -634,7 +648,7 @@ pub mod pallet { } impl Pallet { - /// Calculate the address of an anonymous account. + /// Calculate the address of a keyless account. /// /// - `who`: The spawner account. /// - `proxy_type`: The type of the proxy that the sender will be registered as over the @@ -643,9 +657,9 @@ impl Pallet { /// - `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`. - /// - `maybe_when`: The block height and extrinsic index of when the anonymous account was + /// - `maybe_when`: The block height and extrinsic index of when the keyless account was /// created. None to use current block height and extrinsic index. - pub fn anonymous_account( + pub fn keyless_account( who: &T::AccountId, proxy_type: &T::ProxyType, index: u16, @@ -663,6 +677,16 @@ impl Pallet { .expect("infinite length input; no invalid inputs for type; qed") } + fn is_vain_enough(account: &T::AccountId) -> bool { + let vanity_len = T::MaxVanity::get() as usize; + account + .as_ref() + .get(vanity_len..) + .expect("MaxVanity shorter than account") + .iter() + .all(|b| b == &0) + } + /// Register a proxy account for the delegator that is able to make calls on its behalf. /// /// Parameters: @@ -799,8 +823,8 @@ impl Pallet { force_proxy_type: Option, ) -> Result, DispatchError> { let f = |x: &ProxyDefinition| -> bool { - &x.delegate == delegate && - force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y) + &x.delegate == delegate + && force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y) }; Ok(Proxies::::get(real).0.into_iter().find(f).ok_or(Error::::NotProxy)?) } @@ -818,15 +842,19 @@ impl Pallet { match c.is_sub_type() { // Proxy call cannot add or remove a proxy with more permissions than it already // has. - Some(Call::add_proxy { ref proxy_type, .. }) | - Some(Call::remove_proxy { ref proxy_type, .. }) + Some(Call::add_proxy { ref proxy_type, .. }) + | Some(Call::remove_proxy { ref proxy_type, .. }) if !def.proxy_type.is_superset(&proxy_type) => - false, - // Proxy call cannot remove all proxies or kill anonymous proxies unless it has full + { + false + }, + // Proxy call cannot remove all proxies or kill keyless accounts unless it has full // permissions. - Some(Call::remove_proxies { .. }) | Some(Call::kill_anonymous { .. }) + Some(Call::remove_proxies { .. }) | Some(Call::kill_keyless { .. }) if def.proxy_type != T::ProxyType::default() => - false, + { + false + }, _ => def.proxy_type.filter(c), } }); @@ -834,3 +862,4 @@ impl Pallet { Self::deposit_event(Event::ProxyExecuted { result: e.map(|_| ()).map_err(|e| e.error) }); } } +use_vanitywith_vanity: use_vanitywith_vanity: diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index a0807f1d3d0b6..6d1899bc15163 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -27,7 +27,7 @@ use frame_support::{ assert_noop, assert_ok, dispatch::DispatchError, parameter_types, - traits::{ConstU32, ConstU64, Contains}, + traits::{ConstU32, ConstU64, ConstU8, Contains}, RuntimeDebug, }; use sp_core::H256; @@ -160,6 +160,7 @@ impl Config for Test { type WeightInfo = (); type CallHasher = BlakeTwo256; type MaxPending = ConstU32<2>; + type MaxVanity = ConstU8<10>; type AnnouncementDepositBase = ConstU64<1>; type AnnouncementDepositFactor = ConstU64<1>; } @@ -537,13 +538,13 @@ fn proxying_works() { } #[test] -fn anonymous_works() { +fn keyless_works() { new_test_ext().execute_with(|| { - assert_ok!(Proxy::anonymous(Origin::signed(1), ProxyType::Any, 0, 0)); - let anon = Proxy::anonymous_account(&1, &ProxyType::Any, 0, None); + assert_ok!(Proxy::proxied_keyless(Origin::signed(1), ProxyType::Any, 0, 0)); + let anon = Proxy::keyless_account(&1, &ProxyType::Any, 0, None); System::assert_last_event( - ProxyEvent::AnonymousCreated { - anonymous: anon.clone(), + ProxyEvent::KeylessCreated { + keyless: anon.clone(), who: 1, proxy_type: ProxyType::Any, disambiguation_index: 0, @@ -551,20 +552,20 @@ fn anonymous_works() { .into(), ); - // other calls to anonymous allowed as long as they're not exactly the same. - assert_ok!(Proxy::anonymous(Origin::signed(1), ProxyType::JustTransfer, 0, 0)); - assert_ok!(Proxy::anonymous(Origin::signed(1), ProxyType::Any, 0, 1)); - let anon2 = Proxy::anonymous_account(&2, &ProxyType::Any, 0, None); - assert_ok!(Proxy::anonymous(Origin::signed(2), ProxyType::Any, 0, 0)); + // other calls to keyless allowed as long as they're not exactly the same. + assert_ok!(Proxy::proxied_keyless(Origin::signed(1), ProxyType::JustTransfer, 0, 0)); + assert_ok!(Proxy::proxied_keyless(Origin::signed(1), ProxyType::Any, 0, 1)); + let anon2 = Proxy::keyless_account(&2, &ProxyType::Any, 0, None); + assert_ok!(Proxy::proxied_keyless(Origin::signed(2), ProxyType::Any, 0, 0)); assert_noop!( - Proxy::anonymous(Origin::signed(1), ProxyType::Any, 0, 0), + Proxy::proxied_keyless(Origin::signed(1), ProxyType::Any, 0, 0), Error::::Duplicate ); System::set_extrinsic_index(1); - assert_ok!(Proxy::anonymous(Origin::signed(1), ProxyType::Any, 0, 0)); + assert_ok!(Proxy::proxied_keyless(Origin::signed(1), ProxyType::Any, 0, 0)); System::set_extrinsic_index(0); System::set_block_number(2); - assert_ok!(Proxy::anonymous(Origin::signed(1), ProxyType::Any, 0, 0)); + assert_ok!(Proxy::proxied_keyless(Origin::signed(1), ProxyType::Any, 0, 0)); let call = Box::new(call_transfer(6, 1)); assert_ok!(Balances::transfer(Origin::signed(3), anon, 5)); @@ -572,7 +573,7 @@ fn anonymous_works() { System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); assert_eq!(Balances::free_balance(6), 1); - let call = Box::new(Call::Proxy(ProxyCall::new_call_variant_kill_anonymous( + let call = Box::new(Call::Proxy(ProxyCall::new_call_variant_kill_keyless( 1, ProxyType::Any, 0, @@ -583,7 +584,7 @@ fn anonymous_works() { let de = DispatchError::from(Error::::NoPermission).stripped(); System::assert_last_event(ProxyEvent::ProxyExecuted { result: Err(de) }.into()); assert_noop!( - Proxy::kill_anonymous(Origin::signed(1), 1, ProxyType::Any, 0, 1, 0), + Proxy::kill_keyless(Origin::signed(1), 1, ProxyType::Any, 0, 1, 0), Error::::NoPermission ); assert_eq!(Balances::free_balance(1), 0); diff --git a/frame/proxy/src/weights.rs b/frame/proxy/src/weights.rs index 176f8683e0b5f..96ff967db962a 100644 --- a/frame/proxy/src/weights.rs +++ b/frame/proxy/src/weights.rs @@ -54,8 +54,8 @@ pub trait WeightInfo { fn add_proxy(p: u32, ) -> Weight; fn remove_proxy(p: u32, ) -> Weight; fn remove_proxies(p: u32, ) -> Weight; - fn anonymous(p: u32, ) -> Weight; - fn kill_anonymous(p: u32, ) -> Weight; + fn proxied_keyless(p: u32, ) -> Weight; + fn kill_keyless(p: u32, ) -> Weight; } /// Weights for pallet_proxy using the Substrate node and recommended hardware. @@ -140,7 +140,7 @@ impl WeightInfo for SubstrateWeight { } // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) // Storage: Proxy Proxies (r:1 w:1) - fn anonymous(p: u32, ) -> Weight { + fn proxied_keyless(p: u32, ) -> Weight { (25_743_000 as Weight) // Standard Error: 2_000 .saturating_add((25_000 as Weight).saturating_mul(p as Weight)) @@ -148,7 +148,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Proxy Proxies (r:1 w:1) - fn kill_anonymous(p: u32, ) -> Weight { + fn kill_keyless(p: u32, ) -> Weight { (20_484_000 as Weight) // Standard Error: 2_000 .saturating_add((105_000 as Weight).saturating_mul(p as Weight)) @@ -238,7 +238,7 @@ impl WeightInfo for () { } // Storage: unknown [0x3a65787472696e7369635f696e646578] (r:1 w:0) // Storage: Proxy Proxies (r:1 w:1) - fn anonymous(p: u32, ) -> Weight { + fn proxied_keyless(p: u32, ) -> Weight { (25_743_000 as Weight) // Standard Error: 2_000 .saturating_add((25_000 as Weight).saturating_mul(p as Weight)) @@ -246,7 +246,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Proxy Proxies (r:1 w:1) - fn kill_anonymous(p: u32, ) -> Weight { + fn kill_keyless(p: u32, ) -> Weight { (20_484_000 as Weight) // Standard Error: 2_000 .saturating_add((105_000 as Weight).saturating_mul(p as Weight)) diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 9b30e7b452276..9fde2062b6659 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -275,6 +275,7 @@ pub mod pallet { + Debug + MaybeDisplay + Ord + + AsRef<[u8]> + MaxEncodedLen; /// Converting trait to take a source type and convert to `AccountId`. @@ -1193,7 +1194,7 @@ impl Pallet { let block_number = Self::block_number(); // Don't populate events on genesis. if block_number.is_zero() { - return + return; } let phase = ExecutionPhase::::get().unwrap_or_default(); @@ -1615,7 +1616,7 @@ impl StoredMap for Pallet { }, } } else if !was_providing && !is_providing { - return Ok(result) + return Ok(result); } Account::::mutate(k, |a| a.data = some_data.unwrap_or_default()); Ok(result)