Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ADR 004 Implementation #5572

Merged
merged 25 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
eefd5ec
Initial commit
alexanderbez Jan 27, 2020
5af9e4b
Update CHANGELOG
alexanderbez Jan 27, 2020
9855200
Implement and call ValidateBalance in x/bank InitGenesis
alexanderbez Jan 27, 2020
4d52eb3
Staking updates
alexanderbez Jan 27, 2020
b568331
Update mock
alexanderbez Jan 27, 2020
1a51942
Updates to gov and slashing
alexanderbez Jan 27, 2020
5bda0df
More updates
alexanderbez Jan 27, 2020
2846b04
Update simapp
alexanderbez Jan 27, 2020
c42f422
Set bank keeper in distribution constructor
alexanderbez Jan 27, 2020
2f86f6e
Fix error return
alexanderbez Jan 28, 2020
4f17175
Update godoc on LockedCoins
alexanderbez Jan 29, 2020
84a788e
Update CLI querying
alexanderbez Jan 29, 2020
5be12a9
Update x/auth/vesting/types/vesting_account.go
alexanderbez Jan 30, 2020
ac0aa8a
Remove uneccessary comment
alexanderbez Jan 30, 2020
0b78a9a
Remove uneccessary negative check in AddCoins
alexanderbez Jan 30, 2020
01adcda
Update ValidateBalance godoc
alexanderbez Jan 30, 2020
eba6f27
Update LockedCoins to not depend on balance input/simplify logic
alexanderbez Jan 30, 2020
0aeebc7
Update vesting spec
alexanderbez Jan 30, 2020
636f0e0
Adjust wording
alexanderbez Jan 30, 2020
519e290
Adjust spec
alexanderbez Jan 30, 2020
62540e6
Adjust spec
alexanderbez Jan 30, 2020
d8eb4b7
Update ADR 004
alexanderbez Jan 30, 2020
4290946
Merge PR #5580: ADR 004 Implementation II (Unit Tests & Misc.)
alexanderbez Jan 30, 2020
ac33b1e
Merge branch 'master' into bez/5519-adr-004-bank-balances
alexanderbez Jan 30, 2020
148c9fd
Fix tests
alexanderbez Jan 30, 2020
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
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,29 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

### Client Breaking

* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) The `/bank/balances/{address}` endpoint now returns all account
balances or a single balance by denom when the `denom` query parameter is present.

### API Breaking Changes

* (modules) [\#5555](https://github.com/cosmos/cosmos-sdk/pull/5555) Move x/auth/client/utils/ types and functions to x/auth/client/.
* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Move account balance logic and APIs from `x/auth` to `x/bank`.

### Bug Fixes

* (x/bank) [\#5531](https://github.com/cosmos/cosmos-sdk/issues/5531) Added missing amount event to MsgMultiSend, emitted for each output.

### State Machine Breaking

* (modules) [\#5572](https://github.com/cosmos/cosmos-sdk/pull/5572) Separate balance from accounts per ADR 004.
* Account balances are now persisted and retrieved via the `x/bank` module.
* Vesting account interface has been modified to account for changes.
* Callers to `NewBaseVestingAccount` are responsible for verifying account balance in relation to
the original vesting amount.
* The `SendKeeper` and `ViewKeeper` interfaces in `x/bank` have been modified to account for changes.

### Improvements

* (types) [\#5581](https://github.com/cosmos/cosmos-sdk/pull/5581) Add convenience functions {,Must}Bech32ifyAddressBytes.
Expand Down
77 changes: 60 additions & 17 deletions docs/architecture/adr-004-split-denomination-keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- 2020-01-08: Initial version
- 2020-01-09: Alterations to handle vesting accounts
- 2020-01-14: Updates from review feedback
- 2020-01-30: Updates from implementation

## Context

Expand All @@ -18,35 +19,74 @@ Balances shall be stored per-account & per-denomination under a denomination- an

### Account interface (x/auth)

`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will now be stored in & managed by the bank module.
`GetCoins()` and `SetCoins()` will be removed from the account interface, since coin balances will
now be stored in & managed by the bank module.

`SpendableCoinsVestingAccount()` and `TrackDelegation()` will be altered to take a bank keeper and a denomination as two additional arguments, which will be used to lookup the balances from the base account as necessary.
The vesting account interface will replace `SpendableCoins` in favor of `LockedCoins` which does
not require the account balance anymore. In addition, `TrackDelegation()` will now accept the
account balance of all tokens denominated in the vesting balance instead of loading the entire
account balance.

Vesting accounts will continue to store original vesting, delegated free, and delegated vesting coins (which is safe since these cannot contain arbitrary denominations).
Vesting accounts will continue to store original vesting, delegated free, and delegated
vesting coins (which is safe since these cannot contain arbitrary denominations).

### Bank keeper (x/bank)

`GetBalance(addr AccAddress, denom string) sdk.Coin` and `SetBalance(addr AccAddress, coin sdk.Coin)` methods will be added to the bank keeper to retrieve & set balances, respectively.
The following APIs will be added to the `x/bank` keeper:

Balances will be stored first by the address, then by the denomination (the reverse is also possible, but retrieval of all balances for a single account is presumed to be more frequent):
- `GetAllBalances(ctx Context, addr AccAddress) Coins`
- `GetBalance(ctx Context, addr AccAddress, denom string) Coin`
- `SetBalance(ctx Context, addr AccAddress, coin Coin)`
- `LockedCoins(ctx Context, addr AccAddress) Coins`
- `SpendableCoins(ctx Context, addr AccAddress) Coins`

Additional APIs may be added to facilitate iteration and auxiliary functionality not essential to
core functionality or persistence.

Balances will be stored first by the address, then by the denomination (the reverse is also possible,
but retrieval of all balances for a single account is presumed to be more frequent):

```golang
func BalanceKey(addr sdk.AccAddress, denom string) []byte {
return append(append(BalanceKeyPrefix, addr.Bytes()...), []byte(denom)...)
var BalancesPrefix = []byte("balances")

func (k Keeper) SetBalance(ctx Context, addr AccAddress, balance Coin) error {
if !balance.IsValid() {
return err
}

store := ctx.KVStore(k.storeKey)
balancesStore := prefix.NewStore(store, BalancesPrefix)
accountStore := prefix.NewStore(balancesStore, addr.Bytes())

bz := Marshal(balance)
accountStore.Set([]byte(balance.Denom), bz)

return nil
}
```

`DelegateCoins()` and `UndelegateCoins()` will be altered to take a single `sdk.Coin` (one denomination & amount) instead of `sdk.Coins`, since they should only operate on one denomination. They will read balances directly instead of calling `GetCoins()` (which no longer exists).
This will result in the balances being indexed by the byte representation of
`balances/{address}/{denom}`.

`DelegateCoins()` and `UndelegateCoins()` will be altered to only load each individual
account balance by denomination found in the (un)delegation amount. As a result,
any mutations to the account balance by will made by denomination.

`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist).
`SubtractCoins()` and `AddCoins()` will be altered to read & write the balances
directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist).

`trackDelegation()` and `trackUndelegation()` will be altered to read & write the balances directly instead of calling `GetCoins()` / `SetCoins()` (which no longer exist).
`trackDelegation()` and `trackUndelegation()` will be altered to no longer update
account balances.

External APIs will need to scan all balances under an account to retain backwards-compatibility - additional methods should be added to fetch a balance for a single denomination only.
External APIs will need to scan all balances under an account to retain backwards-compatibility. It
is advised that these APIs use `GetBalance` and `SetBalance` instead of `GetAllBalances` when
possible as to not load the entire account balance.

### Supply module

The supply module, in order to implement the total supply invariant, will now need to scan all accounts & call `GetBalance` using the `x/bank` Keeper for the denomination in question, then sum the balances and check that they match the expected total supply.
The supply module, in order to implement the total supply invariant, will now need
to scan all accounts & call `GetAllBalances` using the `x/bank` Keeper, then sum
the balances and check that they match the expected total supply.

## Status

Expand All @@ -56,18 +96,21 @@ Proposed.

### Positive

- O(1) reads & writes of balances (with respect to the number of denominations for which an account has non-zero balances)
- O(1) reads & writes of balances (with respect to the number of denominations for
which an account has non-zero balances). Note, this does not relate to the actual
I/O cost, rather the total number of direct reads needed.

### Negative

- Slighly less efficient reads/writes when reading & writing all balances of a single account in a transaction.
- Slightly less efficient reads/writes when reading & writing all balances of a
single account in a transaction.

### Neutral

None in particular.

## References

Ref https://github.com/cosmos/cosmos-sdk/issues/4982
Ref https://github.com/cosmos/cosmos-sdk/issues/5467
Ref https://github.com/cosmos/cosmos-sdk/issues/5492
- Ref: https://github.com/cosmos/cosmos-sdk/issues/4982
- Ref: https://github.com/cosmos/cosmos-sdk/issues/5467
- Ref: https://github.com/cosmos/cosmos-sdk/issues/5492
28 changes: 14 additions & 14 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func NewSimApp(
bApp.SetAppVersion(version.Version)

keys := sdk.NewKVStoreKeys(
bam.MainStoreKey, auth.StoreKey, staking.StoreKey,
bam.MainStoreKey, auth.StoreKey, bank.StoreKey, staking.StoreKey,
supply.StoreKey, mint.StoreKey, distr.StoreKey, slashing.StoreKey,
gov.StoreKey, params.StoreKey, upgrade.StoreKey, evidence.StoreKey,
)
Expand Down Expand Up @@ -174,20 +174,20 @@ func NewSimApp(
app.cdc, keys[auth.StoreKey], app.subspaces[auth.ModuleName], auth.ProtoBaseAccount,
)
app.BankKeeper = bank.NewBaseKeeper(
app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(),
app.cdc, keys[bank.StoreKey], app.AccountKeeper, app.subspaces[bank.ModuleName], app.BlacklistedAccAddrs(),
)
app.SupplyKeeper = supply.NewKeeper(
app.cdc, keys[supply.StoreKey], app.AccountKeeper, app.BankKeeper, maccPerms,
)
stakingKeeper := staking.NewKeeper(
app.cdc, keys[staking.StoreKey], app.SupplyKeeper, app.subspaces[staking.ModuleName],
app.cdc, keys[staking.StoreKey], app.BankKeeper, app.SupplyKeeper, app.subspaces[staking.ModuleName],
)
app.MintKeeper = mint.NewKeeper(
app.cdc, keys[mint.StoreKey], app.subspaces[mint.ModuleName], &stakingKeeper,
app.SupplyKeeper, auth.FeeCollectorName,
)
app.DistrKeeper = distr.NewKeeper(
app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], &stakingKeeper,
app.cdc, keys[distr.StoreKey], app.subspaces[distr.ModuleName], app.BankKeeper, &stakingKeeper,
app.SupplyKeeper, auth.FeeCollectorName, app.ModuleAccountAddrs(),
)
app.SlashingKeeper = slashing.NewKeeper(
Expand Down Expand Up @@ -231,12 +231,12 @@ func NewSimApp(
auth.NewAppModule(app.AccountKeeper),
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
crisis.NewAppModule(&app.CrisisKeeper),
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper),
supply.NewAppModule(app.SupplyKeeper, app.BankKeeper, app.AccountKeeper),
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper),
mint.NewAppModule(app.MintKeeper),
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper),
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper),
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper, app.StakingKeeper),
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper),
upgrade.NewAppModule(app.UpgradeKeeper),
evidence.NewAppModule(app.EvidenceKeeper),
)
Expand Down Expand Up @@ -265,12 +265,12 @@ func NewSimApp(
app.sm = module.NewSimulationManager(
auth.NewAppModule(app.AccountKeeper),
bank.NewAppModule(app.BankKeeper, app.AccountKeeper),
supply.NewAppModule(app.SupplyKeeper, app.AccountKeeper),
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.SupplyKeeper),
supply.NewAppModule(app.SupplyKeeper, app.BankKeeper, app.AccountKeeper),
gov.NewAppModule(app.GovKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper),
mint.NewAppModule(app.MintKeeper),
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.SupplyKeeper),
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.SupplyKeeper, app.StakingKeeper),
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.StakingKeeper),
staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper),
distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper, app.StakingKeeper),
slashing.NewAppModule(app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
params.NewAppModule(), // NOTE: only used for simulation to generate randomized param change proposals
)

Expand Down
3 changes: 0 additions & 3 deletions simapp/genesis_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ type SimGenesisAccount struct {
// Validate checks for errors on the vesting and module account parameters
func (sga SimGenesisAccount) Validate() error {
if !sga.OriginalVesting.IsZero() {
if sga.OriginalVesting.IsAnyGT(sga.Coins) {
return errors.New("vesting amount cannot be greater than total amount")
}
if sga.StartTime >= sga.EndTime {
return errors.New("vesting start-time cannot be before end-time")
}
Expand Down
17 changes: 3 additions & 14 deletions simapp/genesis_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ func TestSimGenesisAccountValidate(t *testing.T) {
vestingStart := time.Now().UTC()

coins := sdk.NewCoins(sdk.NewInt64Coin("test", 1000))
baseAcc := authtypes.NewBaseAccount(addr, nil, pubkey, 0, 0)
require.NoError(t, baseAcc.SetCoins(coins))
baseAcc := authtypes.NewBaseAccount(addr, pubkey, 0, 0)

testCases := []struct {
name string
Expand All @@ -38,14 +37,14 @@ func TestSimGenesisAccountValidate(t *testing.T) {
{
"invalid basic account with mismatching address/pubkey",
simapp.SimGenesisAccount{
BaseAccount: authtypes.NewBaseAccount(addr, nil, secp256k1.GenPrivKey().PubKey(), 0, 0),
BaseAccount: authtypes.NewBaseAccount(addr, secp256k1.GenPrivKey().PubKey(), 0, 0),
},
true,
},
{
"valid basic account with module name",
simapp.SimGenesisAccount{
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(crypto.AddressHash([]byte("testmod"))), nil, nil, 0, 0),
BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(crypto.AddressHash([]byte("testmod"))), nil, 0, 0),
ModuleName: "testmod",
},
false,
Expand Down Expand Up @@ -78,16 +77,6 @@ func TestSimGenesisAccountValidate(t *testing.T) {
},
true,
},
{
"valid basic account with invalid original vesting coins",
simapp.SimGenesisAccount{
BaseAccount: baseAcc,
OriginalVesting: coins.Add(coins...),
StartTime: vestingStart.Unix(),
EndTime: vestingStart.Add(1 * time.Hour).Unix(),
},
true,
},
}

for _, tc := range testCases {
Expand Down
9 changes: 3 additions & 6 deletions simapp/test_helpers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package simapp

import (
"os"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -48,7 +47,7 @@ func Setup(isCheckTx bool) *SimApp {
// genesis accounts.
func SetupWithGenesisAccounts(genAccs []authexported.GenesisAccount) *SimApp {
db := dbm.NewMemDB()
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0)
app := NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0)

// initialize the chain with the passed in genesis accounts
genesisState := NewDefaultGenesisState()
Expand Down Expand Up @@ -101,11 +100,9 @@ func AddTestAddrs(app *SimApp, ctx sdk.Context, accNum int, accAmt sdk.Int) []sd
}

// CheckBalance checks the balance of an account.
func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, exp sdk.Coins) {
func CheckBalance(t *testing.T, app *SimApp, addr sdk.AccAddress, balances sdk.Coins) {
ctxCheck := app.BaseApp.NewContext(true, abci.Header{})
res := app.AccountKeeper.GetAccount(ctxCheck, addr)

require.True(t, exp.IsEqual(res.GetCoins()))
require.True(t, balances.IsEqual(app.BankKeeper.GetAllBalances(ctxCheck, addr)))
}

// SignCheckDeliver checks a generated signed transaction and simulates a
Expand Down
4 changes: 1 addition & 3 deletions store/prefix/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ import (

// copied from iavl/store_test.go
var (
cacheSize = 100
numRecent int64 = 5
storeEvery int64 = 3
cacheSize = 100
)

func bz(s string) []byte { return []byte(s) }
Expand Down
Loading