Account data structures
Accounts are a fundamental part of any blockchain, and, in Substrate, accounts can be used in many different ways. This article describes how accounts are stored in Substrate and how the account data structure is used to manage the account lifecycle in the runtime logic.
Account
The Account
data type is a storage map that is defined generically in the frame-system
pallet:
/// The full account information for a particular account ID.
#[pallet::storage]
#[pallet::getter(fn account)]
pub type Account<T: Config> = StorageMap<
_,
Blake2_128Concat,
T::AccountId,
AccountInfo<T::Nonce, T::AccountData>,
ValueQuery,
>;
The StorageMap
for an Account
consists of the following parameters:
- The first parameter (_) is used in macro expansion.
Blake2_128Concat
specifies the hashing algorithm to use.T::AccountId
is used as the key for over theAccountInfo<T::Nonce, T::AccountData>
struct.
See StorageMap
API for details.
AccountInfo
The AccountInfo
for an account is defined in the frame_system
pallet:
#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
pub struct AccountInfo<Nonce, AccountData> {
/// The number of transactions this account has sent.
pub nonce: Nonce,
/// The number of other modules that currently depend on this account's existence. The account
/// cannot be reaped until this is zero.
pub consumers: RefCount,
/// The number of other modules that allow this account to exist. The account may not be reaped
/// until this and `sufficients` are both zero.
pub providers: RefCount,
/// The number of modules that allow this account to exist for their own purposes only. The
/// account may not be reaped until this and `providers` are both zero.
pub sufficients: RefCount,
/// The additional data that belongs to this account. Used to store the balance(s) in a lot of
/// chains.
pub data: AccountData,
}
Every account has an AccountInfo
consisting of:
- The
nonce
indicating the number of transactions the account has sent. - The
consumers
reference counter indicating the number of other modules that currently depend on this account's existence. - The
providers
reference counter indicating the number of other modules that allow this account to exist. - The
sufficients
reference counter indicating the number of modules that allow this account to exist for their own purposes only. - The
AccountData
structure that you can configure to hold different kinds of data.
Account reference counters
The account reference counters track account dependencies in the runtime. For example, if you store data under a map controlled by an account, you wouldn't want to delete the account until the data stored under the map the account controls has been deleted.
The consumers
and providers
reference counters are designed to be used together.
For example, the consumers
reference counter in the Session
pallet is incremented when an account sets its session keys prior of becoming a validator.
The providers
reference counter must be greater than zero before the consumer
counter can be incremented.
The providers
reference counter indicates if an account is ready to be depended upon.
For example, the providers
reference counter is incremented when a new account is created with more than the existential deposit [2].
The providers
reference counter prevents Substrate pallets from storing data about an account until the account is active (providers
> 0).
The consumers
reference counter prevents Substrate pallets from removing an account until data about the account is removed in all pallets (consumers
== 0).
The consumers
reference counter holds users accountable for the data stored on-chain.
If users want to remove their accounts and get back the existential deposit, they need to remove all of the data they have stored on all on-chain pallets to decrement consumers
counter.
Pallets also have cleanup functions to decrement the providers
reference counter to mark the account as deactivated within the pallet-managed scope.
When the account providers
reference counter is zero and the consumers
is zero, the account is considered deactivated by all on-chain pallets.
The sufficients
reference counter indicates if an account is self-sufficient and can exist by itself.
For example, in the Assets pallet, an account can have sufficient number of certain assets but without owning any native account balance.
Runtime developers can update these counters using the inc_consumers()
, dec_consumers()
, inc_providers()
, dec_providers()
, inc_sufficients()
, and dec_sufficients()
methods exposed by the frame-system
pallet.
Each increment call of a certain counter should be accompanied by a corresponding decrement call of the counter in an account life cycle.
There are also three query functions to ease usage on these counters:
can_inc_consumer()
to check if an account is ready to be used (providers
> 0).can_dec_provider()
to check if an account is no longer referenced in runtime whatsoever (consumers
== 0) before decrementingproviders
to 0.is_provider_required()
to check if an account has outstanding consumer references (consumers
> 0).
See frame-system
API for details.
AccountData trait and implementation
The AccountInfo
can be any struct as long as the struct satisfies the associated type AccountData
trait bound defined in the frame-system::pallet::Config
trait.
By default, the Substrate runtime configures AccountInfo
to be as defined in pallet-balances
.
Where to go next
For addtional technical details, see the following resources: