github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-004-split-denomination-keys.md (about)

     1  # ADR 004: Split Denomination Keys
     2  
     3  ## Changelog
     4  
     5  * 2020-01-08: Initial version
     6  * 2020-01-09: Alterations to handle vesting accounts
     7  * 2020-01-14: Updates from review feedback
     8  * 2020-01-30: Updates from implementation
     9  
    10  ### Glossary
    11  
    12  * denom / denomination key -- unique token identifier.
    13  
    14  ## Context
    15  
    16  With permissionless IBC, anyone will be able to send arbitrary denominations to any other account. Currently, all non-zero balances are stored along with the account in an `sdk.Coins` struct, which creates a potential denial-of-service concern, as too many denominations will become expensive to load & store each time the account is modified. See issues [5467](https://github.com/cosmos/cosmos-sdk/issues/5467) and [4982](https://github.com/cosmos/cosmos-sdk/issues/4982) for additional context.
    17  
    18  Simply rejecting incoming deposits after a denomination count limit doesn't work, since it opens up a griefing vector: someone could send a user lots of nonsensical coins over IBC, and then prevent the user from receiving real denominations (such as staking rewards).
    19  
    20  ## Decision
    21  
    22  Balances shall be stored per-account & per-denomination under a denomination- and account-unique key, thus enabling O(1) read & write access to the balance of a particular account in a particular denomination.
    23  
    24  ### Account interface (x/auth)
    25  
    26  `GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will
    27  now be stored in & managed by the bank module.
    28  
    29  The vesting account interface will replace `SpendableCoins` in favor of `LockedCoins` which does
    30  not require the account balance anymore. In addition, `TrackDelegation()`  will now accept the
    31  account balance of all tokens denominated in the vesting balance instead of loading the entire
    32  account balance.
    33  
    34  Vesting accounts will continue to store original vesting, delegated free, and delegated
    35  vesting coins (which is safe since these cannot contain arbitrary denominations).
    36  
    37  ### Bank keeper (x/bank)
    38  
    39  The following APIs will be added to the `x/bank` keeper:
    40  
    41  * `GetAllBalances(ctx Context, addr AccAddress) Coins`
    42  * `GetBalance(ctx Context, addr AccAddress, denom string) Coin`
    43  * `SetBalance(ctx Context, addr AccAddress, coin Coin)`
    44  * `LockedCoins(ctx Context, addr AccAddress) Coins`
    45  * `SpendableCoins(ctx Context, addr AccAddress) Coins`
    46  
    47  Additional APIs may be added to facilitate iteration and auxiliary functionality not essential to
    48  core functionality or persistence.
    49  
    50  Balances will be stored first by the address, then by the denomination (the reverse is also possible,
    51  but retrieval of all balances for a single account is presumed to be more frequent):
    52  
    53  ```go
    54  var BalancesPrefix = []byte("balances")
    55  
    56  func (k Keeper) SetBalance(ctx Context, addr AccAddress, balance Coin) error {
    57    if !balance.IsValid() {
    58      return err
    59    }
    60  
    61    store := ctx.KVStore(k.storeKey)
    62    balancesStore := prefix.NewStore(store, BalancesPrefix)
    63    accountStore := prefix.NewStore(balancesStore, addr.Bytes())
    64  
    65    bz := Marshal(balance)
    66    accountStore.Set([]byte(balance.Denom), bz)
    67  
    68    return nil
    69  }
    70  ```
    71  
    72  This will result in the balances being indexed by the byte representation of
    73  `balances/{address}/{denom}`.
    74  
    75  `DelegateCoins()` and `UndelegateCoins()` will be altered to only load each individual
    76  account balance by denomination found in the (un)delegation amount. As a result,
    77  any mutations to the account balance by will made by denomination.
    78  
    79  `SubtractCoins()` and `AddCoins()` will be altered to read & write the balances
    80  directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist).
    81  
    82  `trackDelegation()` and `trackUndelegation()` will be altered to no longer update
    83  account balances.
    84  
    85  External APIs will need to scan all balances under an account to retain backwards-compatibility. It
    86  is advised that these APIs use `GetBalance` and `SetBalance` instead of `GetAllBalances` when
    87  possible as to not load the entire account balance.
    88  
    89  ### Supply module
    90  
    91  The supply module, in order to implement the total supply invariant, will now need
    92  to scan all accounts & call `GetAllBalances` using the `x/bank` Keeper, then sum
    93  the balances and check that they match the expected total supply.
    94  
    95  ## Status
    96  
    97  Accepted.
    98  
    99  ## Consequences
   100  
   101  ### Positive
   102  
   103  * O(1) reads & writes of balances (with respect to the number of denominations for
   104  which an account has non-zero balances). Note, this does not relate to the actual
   105  I/O cost, rather the total number of direct reads needed.
   106  
   107  ### Negative
   108  
   109  * Slightly less efficient reads/writes when reading & writing all balances of a
   110  single account in a transaction.
   111  
   112  ### Neutral
   113  
   114  None in particular.
   115  
   116  ## References
   117  
   118  * Ref: https://github.com/cosmos/cosmos-sdk/issues/4982
   119  * Ref: https://github.com/cosmos/cosmos-sdk/issues/5467
   120  * Ref: https://github.com/cosmos/cosmos-sdk/issues/5492