// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! An index is a short form of an address. This module handles allocation
//! of indices for a newly created accounts.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::prelude::*;
use codec::Codec;
use sp_runtime::traits::{
StaticLookup, Member, LookupError, Zero, One, BlakeTwo256, Hash, Saturating, AtLeast32Bit
};
use frame_support::{Parameter, decl_module, decl_error, decl_event, decl_storage, ensure};
use frame_support::weights::{Weight, MINIMUM_WEIGHT};
use frame_support::dispatch::DispatchResult;
use frame_support::traits::{Currency, ReservableCurrency, Get, BalanceStatus::Reserved};
use frame_support::storage::migration::take_storage_value;
use frame_system::{ensure_signed, ensure_root};
use self::address::Address as RawAddress;
mod mock;
pub mod address;
mod tests;
pub type Address = RawAddress<::AccountId, ::AccountIndex>;
type BalanceOf = <::Currency as Currency<::AccountId>>::Balance;
/// The module's config trait.
pub trait Trait: frame_system::Trait {
/// Type used for storing an account's index; implies the maximum number of accounts the system
/// can hold.
type AccountIndex: Parameter + Member + Codec + Default + AtLeast32Bit + Copy;
/// The currency trait.
type Currency: ReservableCurrency;
/// The deposit needed for reserving an index.
type Deposit: Get>;
/// The overarching event type.
type Event: From> + Into<::Event>;
}
decl_storage! {
trait Store for Module as Indices {
/// The lookup from index to account.
pub Accounts build(|config: &GenesisConfig|
config.indices.iter()
.cloned()
.map(|(a, b)| (a, (b, Zero::zero())))
.collect::>()
): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf)>;
}
add_extra_genesis {
config(indices): Vec<(T::AccountIndex, T::AccountId)>;
}
}
decl_event!(
pub enum Event where
::AccountId,
::AccountIndex
{
/// A account index was assigned.
IndexAssigned(AccountId, AccountIndex),
/// A account index has been freed up (unassigned).
IndexFreed(AccountIndex),
}
);
decl_error! {
pub enum Error for Module {
/// The index was not already assigned.
NotAssigned,
/// The index is assigned to another account.
NotOwner,
/// The index was not available.
InUse,
/// The source and destination accounts are identical.
NotTransfer,
}
}
decl_module! {
pub struct Module for enum Call where origin: T::Origin, system = frame_system {
fn deposit_event() = default;
fn on_initialize() -> Weight {
Self::migrations();
MINIMUM_WEIGHT
}
/// Assign an previously unassigned index.
///
/// Payment: `Deposit` is reserved from the sender account.
///
/// The dispatch origin for this call must be _Signed_.
///
/// - `index`: the index to be claimed. This must not be in use.
///
/// Emits `IndexAssigned` if successful.
///
/// #
/// - `O(1)`.
/// - One storage mutation (codec `O(1)`).
/// - One reserve operation.
/// - One event.
/// #
#[weight = MINIMUM_WEIGHT]
fn claim(origin, index: T::AccountIndex) {
let who = ensure_signed(origin)?;
Accounts::::try_mutate(index, |maybe_value| {
ensure!(maybe_value.is_none(), Error::::InUse);
*maybe_value = Some((who.clone(), T::Deposit::get()));
T::Currency::reserve(&who, T::Deposit::get())
})?;
Self::deposit_event(RawEvent::IndexAssigned(who, index));
}
/// Assign an index already owned by the sender to another account. The balance reservation
/// is effectively transferred to the new account.
///
/// The dispatch origin for this call must be _Signed_.
///
/// - `index`: the index to be re-assigned. This must be owned by the sender.
/// - `new`: the new owner of the index. This function is a no-op if it is equal to sender.
///
/// Emits `IndexAssigned` if successful.
///
/// #
/// - `O(1)`.
/// - One storage mutation (codec `O(1)`).
/// - One transfer operation.
/// - One event.
/// #
#[weight = MINIMUM_WEIGHT]
fn transfer(origin, new: T::AccountId, index: T::AccountIndex) {
let who = ensure_signed(origin)?;
ensure!(who != new, Error::::NotTransfer);
Accounts::::try_mutate(index, |maybe_value| -> DispatchResult {
let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?;
ensure!(&account == &who, Error::::NotOwner);
let lost = T::Currency::repatriate_reserved(&who, &new, amount, Reserved)?;
*maybe_value = Some((new.clone(), amount.saturating_sub(lost)));
Ok(())
})?;
Self::deposit_event(RawEvent::IndexAssigned(new, index));
}
/// Free up an index owned by the sender.
///
/// Payment: Any previous deposit placed for the index is unreserved in the sender account.
///
/// The dispatch origin for this call must be _Signed_ and the sender must own the index.
///
/// - `index`: the index to be freed. This must be owned by the sender.
///
/// Emits `IndexFreed` if successful.
///
/// #
/// - `O(1)`.
/// - One storage mutation (codec `O(1)`).
/// - One reserve operation.
/// - One event.
/// #
#[weight = MINIMUM_WEIGHT]
fn free(origin, index: T::AccountIndex) {
let who = ensure_signed(origin)?;
Accounts::::try_mutate(index, |maybe_value| -> DispatchResult {
let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?;
ensure!(&account == &who, Error::::NotOwner);
T::Currency::unreserve(&who, amount);
Ok(())
})?;
Self::deposit_event(RawEvent::IndexFreed(index));
}
/// Force an index to an account. This doesn't require a deposit. If the index is already
/// held, then any deposit is reimbursed to its current owner.
///
/// The dispatch origin for this call must be _Root_.
///
/// - `index`: the index to be (re-)assigned.
/// - `new`: the new owner of the index. This function is a no-op if it is equal to sender.
///
/// Emits `IndexAssigned` if successful.
///
/// #
/// - `O(1)`.
/// - One storage mutation (codec `O(1)`).
/// - Up to one reserve operation.
/// - One event.
/// #
#[weight = MINIMUM_WEIGHT]
fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex) {
ensure_root(origin)?;
Accounts::::mutate(index, |maybe_value| {
if let Some((account, amount)) = maybe_value.take() {
T::Currency::unreserve(&account, amount);
}
*maybe_value = Some((new.clone(), Zero::zero()));
});
Self::deposit_event(RawEvent::IndexAssigned(new, index));
}
}
}
impl Module {
// PUBLIC IMMUTABLES
/// Lookup an T::AccountIndex to get an Id, if there's one there.
pub fn lookup_index(index: T::AccountIndex) -> Option {
Accounts::::get(index).map(|x| x.0)
}
/// Lookup an address to get an Id, if there's one there.
pub fn lookup_address(
a: address::Address
) -> Option {
match a {
address::Address::Id(i) => Some(i),
address::Address::Index(i) => Self::lookup_index(i),
}
}
/// Do any migrations.
fn migrations() {
if let Some(set_count) = take_storage_value::(b"Indices", b"NextEnumSet", b"") {
// migrations need doing.
let set_size: T::AccountIndex = 64.into();
let mut set_index: T::AccountIndex = Zero::zero();
while set_index < set_count {
let maybe_accounts = take_storage_value::>(b"Indices", b"EnumSet", BlakeTwo256::hash_of(&set_index).as_ref());
if let Some(accounts) = maybe_accounts {
for (item_index, target) in accounts.into_iter().enumerate() {
if target != T::AccountId::default() && !T::Currency::total_balance(&target).is_zero() {
let index = set_index * set_size + T::AccountIndex::from(item_index as u32);
Accounts::::insert(index, (target, BalanceOf::::zero()));
}
}
} else {
break;
}
set_index += One::one();
}
}
}
}
impl StaticLookup for Module {
type Source = address::Address;
type Target = T::AccountId;
fn lookup(a: Self::Source) -> Result {
Self::lookup_address(a).ok_or(LookupError)
}
fn unlookup(a: Self::Target) -> Self::Source {
address::Address::Id(a)
}
}