Skip to content

Commit

Permalink
Index supply by denom (#8517)
Browse files Browse the repository at this point in the history
* temp commit

* remove supply

* update tests

* revert proto script

* fix lint

* update tests

* remove decoder

* fix lint

* update set supply

* add changelog

Co-authored-by: Jonathan Gimeno <[email protected]>
  • Loading branch information
sahith-narahari and jgimeno authored Mar 3, 2021
1 parent 2864eb6 commit eb8aaf9
Show file tree
Hide file tree
Showing 23 changed files with 152 additions and 539 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (client/keys) [\#8500](https://github.com/cosmos/cosmos-sdk/pull/8500) `InfoImporter` interface is removed from legacy keybase.
* [\#8629](https://github.com/cosmos/cosmos-sdk/pull/8629) Deprecated `SetFullFundraiserPath` from `Config` in favor of `SetPurpose` and `SetCoinType`.
* (x/upgrade) [\#8673](https://github.com/cosmos/cosmos-sdk/pull/8673) Remove IBC logic from x/upgrade. Deprecates IBC fields in an Upgrade Plan. IBC upgrade logic moved to 02-client and an IBC UpgradeProposal is added.
* (x/bank) [\#8517](https://github.com/cosmos/cosmos-sdk/pull/8517) `SupplyI` interface and `Supply` are removed and uses `sdk.Coins` for supply tracking

### State Machine Breaking

Expand All @@ -61,7 +62,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/evidence) [\#8502](https://github.com/cosmos/cosmos-sdk/pull/8502) `HandleEquivocationEvidence` persists the evidence to state.
* (x/gov) [\#7733](https://github.com/cosmos/cosmos-sdk/pull/7733) ADR 037 Implementation: Governance Split Votes
* (x/bank) [\#8656](https://github.com/cosmos/cosmos-sdk/pull/8656) balance and supply are now correctly tracked via `coin_spent`, `coin_received`, `coinbase` and `burn` events.

* (x/bank) [\#8517](https://github.com/cosmos/cosmos-sdk/pull/8517) Supply is now stored and tracked as `sdk.Coins`
### Improvements

* (x/bank) [\#8614](https://github.com/cosmos/cosmos-sdk/issues/8614) Add `Name` and `Symbol` fields to denom metadata
Expand Down
13 changes: 0 additions & 13 deletions proto/cosmos/bank/v1beta1/bank.proto
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,6 @@ message Output {
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
}

// Supply represents a struct that passively keeps track of the total supply
// amounts in the network.
message Supply {
option (gogoproto.equal) = true;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;

option (cosmos_proto.implements_interface) = "*github.com/cosmos/cosmos-sdk/x/bank/exported.SupplyI";

repeated cosmos.base.v1beta1.Coin total = 1
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
}

// DenomUnit represents a struct that describes a given
// denomination unit of the basic token.
message DenomUnit {
Expand Down
17 changes: 0 additions & 17 deletions x/bank/exported/exported.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package exported

import (
"github.com/gogo/protobuf/proto"

sdk "github.com/cosmos/cosmos-sdk/types"
)

Expand All @@ -12,18 +10,3 @@ type GenesisBalance interface {
GetAddress() sdk.AccAddress
GetCoins() sdk.Coins
}

// SupplyI defines an inflationary supply interface for modules that handle
// token supply.
type SupplyI interface {
proto.Message

GetTotal() sdk.Coins
SetTotal(total sdk.Coins)

Inflate(amount sdk.Coins)
Deflate(amount sdk.Coins)

String() string
ValidateBasic() error
}
4 changes: 2 additions & 2 deletions x/bank/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (k BaseKeeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) {
genState.Supply = totalSupply
}

k.setSupply(ctx, types.NewSupply(genState.Supply))
k.setSupply(ctx, genState.Supply)

for _, meta := range genState.DenomMetadata {
k.SetDenomMetaData(ctx, meta)
Expand All @@ -43,7 +43,7 @@ func (k BaseKeeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
return types.NewGenesisState(
k.GetParams(ctx),
k.GetAccountsBalances(ctx),
k.GetSupply(ctx).GetTotal(),
k.GetTotalSupply(ctx),
k.GetAllDenomMetaData(ctx),
)
}
8 changes: 4 additions & 4 deletions x/bank/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ func (suite *IntegrationTestSuite) TestExportGenesis() {

suite.Require().Len(exportGenesis.Params.SendEnabled, 0)
suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled)
suite.Require().Equal(totalSupply.Total, exportGenesis.Supply)
suite.Require().Equal(totalSupply, exportGenesis.Supply)
// add mint module balance as nil
expectedBalances = append(expectedBalances, types.Balance{Address: "cosmos1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q", Coins: nil})
suite.Require().Equal(expectedBalances, exportGenesis.Balances)
suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata)
}

func (suite *IntegrationTestSuite) getTestBalancesAndSupply() ([]types.Balance, *types.Supply) {
func (suite *IntegrationTestSuite) getTestBalancesAndSupply() ([]types.Balance, sdk.Coins) {
addr2, _ := sdk.AccAddressFromBech32("cosmos1f9xjhxm0plzrh9cskf4qee4pc2xwp0n0556gh0")
addr1, _ := sdk.AccAddressFromBech32("cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh")
addr1Balance := sdk.Coins{sdk.NewInt64Coin("testcoin3", 10)}
addr2Balance := sdk.Coins{sdk.NewInt64Coin("testcoin1", 32), sdk.NewInt64Coin("testcoin2", 34)}

totalSupply := types.NewSupply(addr1Balance)
totalSupply.Inflate(addr2Balance)
totalSupply := addr1Balance
totalSupply = totalSupply.Add(addr2Balance...)
return []types.Balance{
{Address: addr2.String(), Coins: addr2Balance},
{Address: addr1.String(), Coins: addr1Balance},
Expand Down
6 changes: 3 additions & 3 deletions x/bank/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances
// TotalSupply implements the Query/TotalSupply gRPC method
func (k BaseKeeper) TotalSupply(ctx context.Context, _ *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
totalSupply := k.GetSupply(sdkCtx).GetTotal()
totalSupply := k.GetTotalSupply(sdkCtx)

return &types.QueryTotalSupplyResponse{Supply: totalSupply}, nil
}
Expand All @@ -95,9 +95,9 @@ func (k BaseKeeper) SupplyOf(c context.Context, req *types.QuerySupplyOfRequest)
}

ctx := sdk.UnwrapSDKContext(c)
supply := k.GetSupply(ctx).GetTotal().AmountOf(req.Denom)
supply := k.GetSupply(ctx, req.Denom)

return &types.QuerySupplyOfResponse{Amount: sdk.NewCoin(req.Denom, supply)}, nil
return &types.QuerySupplyOfResponse{Amount: sdk.NewCoin(req.Denom, supply.Amount)}, nil
}

// Params implements the gRPC service handler for querying x/bank parameters.
Expand Down
12 changes: 5 additions & 7 deletions x/bank/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// +build norace

package keeper_test

import (
Expand Down Expand Up @@ -90,27 +88,27 @@ func (suite *IntegrationTestSuite) TestQueryAllBalances() {

func (suite *IntegrationTestSuite) TestQueryTotalSupply() {
app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient
expectedTotalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000)))
expectedTotalSupply := sdk.NewCoins(sdk.NewInt64Coin("test", 400000000))
suite.
Require().
NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total))
NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply))

res, err := queryClient.TotalSupply(gocontext.Background(), &types.QueryTotalSupplyRequest{})
suite.Require().NoError(err)
suite.Require().NotNil(res)

suite.Require().Equal(expectedTotalSupply.Total, res.Supply)
suite.Require().Equal(expectedTotalSupply, res.Supply)
}

func (suite *IntegrationTestSuite) TestQueryTotalSupplyOf() {
app, ctx, queryClient := suite.app, suite.ctx, suite.queryClient

test1Supply := sdk.NewInt64Coin("test1", 4000000)
test2Supply := sdk.NewInt64Coin("test2", 700000000)
expectedTotalSupply := types.NewSupply(sdk.NewCoins(test1Supply, test2Supply))
expectedTotalSupply := sdk.NewCoins(test1Supply, test2Supply)
suite.
Require().
NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply.Total))
NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, expectedTotalSupply))

_, err := queryClient.SupplyOf(gocontext.Background(), &types.QuerySupplyOfRequest{})
suite.Require().Error(err)
Expand Down
6 changes: 3 additions & 3 deletions x/bank/keeper/invariants.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,19 @@ func NonnegativeBalanceInvariant(k ViewKeeper) sdk.Invariant {
func TotalSupply(k Keeper) sdk.Invariant {
return func(ctx sdk.Context) (string, bool) {
expectedTotal := sdk.Coins{}
supply := k.GetSupply(ctx)
supply := k.GetTotalSupply(ctx)

k.IterateAllBalances(ctx, func(_ sdk.AccAddress, balance sdk.Coin) bool {
expectedTotal = expectedTotal.Add(balance)
return false
})

broken := !expectedTotal.IsEqual(supply.GetTotal())
broken := !expectedTotal.IsEqual(supply)

return sdk.FormatInvariant(types.ModuleName, "total supply",
fmt.Sprintf(
"\tsum of accounts coins: %v\n"+
"\tsupply.Total: %v\n",
expectedTotal, supply.GetTotal())), broken
expectedTotal, supply)), broken
}
}
93 changes: 65 additions & 28 deletions x/bank/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
vestexported "github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
"github.com/cosmos/cosmos-sdk/x/bank/exported"
"github.com/cosmos/cosmos-sdk/x/bank/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
)
Expand All @@ -22,7 +21,9 @@ type Keeper interface {
InitGenesis(sdk.Context, *types.GenesisState)
ExportGenesis(sdk.Context) *types.GenesisState

GetSupply(ctx sdk.Context) exported.SupplyI
GetSupply(ctx sdk.Context, denom string) sdk.Coin
GetTotalSupply(ctx sdk.Context) sdk.Coins
IterateTotalSupply(ctx sdk.Context, cb func(sdk.Coin) bool)

GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool)
SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata)
Expand All @@ -38,8 +39,6 @@ type Keeper interface {

DelegateCoins(ctx sdk.Context, delegatorAddr, moduleAccAddr sdk.AccAddress, amt sdk.Coins) error
UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAddr sdk.AccAddress, amt sdk.Coins) error
MarshalSupply(supplyI exported.SupplyI) ([]byte, error)
UnmarshalSupply(bz []byte) (exported.SupplyI, error)

types.QueryServer
}
Expand All @@ -54,6 +53,16 @@ type BaseKeeper struct {
paramSpace paramtypes.Subspace
}

func (k BaseKeeper) GetTotalSupply(ctx sdk.Context) sdk.Coins {
balances := sdk.NewCoins()
k.IterateTotalSupply(ctx, func(balance sdk.Coin) bool {
balances = balances.Add(balance)
return false
})

return balances.Sort()
}

func NewBaseKeeper(
cdc codec.BinaryMarshaler, storeKey sdk.StoreKey, ak types.AccountKeeper, paramSpace paramtypes.Subspace,
blockedAddrs map[string]bool,
Expand Down Expand Up @@ -154,30 +163,52 @@ func (k BaseKeeper) UndelegateCoins(ctx sdk.Context, moduleAccAddr, delegatorAdd
}

// GetSupply retrieves the Supply from store
func (k BaseKeeper) GetSupply(ctx sdk.Context) exported.SupplyI {
func (k BaseKeeper) GetSupply(ctx sdk.Context, denom string) sdk.Coin {
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.SupplyKey)
supplyStore := prefix.NewStore(store, types.SupplyKey)

bz := supplyStore.Get([]byte(denom))
if bz == nil {
panic("stored supply should not have been nil")
return sdk.Coin{
Denom: denom,
Amount: sdk.NewInt(0),
}
}

supply, err := k.UnmarshalSupply(bz)
var coin sdk.Coin
err := k.cdc.UnmarshalBinaryBare(bz, &coin)
if err != nil {
panic(err)
}

return supply
return coin
}

// setSupply sets the Supply to store
func (k BaseKeeper) setSupply(ctx sdk.Context, supply exported.SupplyI) {
// SetSupply sets the Supply to store
func (k BaseKeeper) setSupply(ctx sdk.Context, supply sdk.Coins) {
store := ctx.KVStore(k.storeKey)
bz, err := k.MarshalSupply(supply)
if err != nil {
panic(err)
supplyStore := prefix.NewStore(store, types.SupplyKey)

var newSupply []sdk.Coin
storeSupply := k.GetTotalSupply(ctx)

// update supply for coins which have non zero amount
for _, coin := range storeSupply {
if supply.AmountOf(coin.Denom).IsZero() {
zeroCoin := &sdk.Coin{
Denom: coin.Denom,
Amount: sdk.NewInt(0),
}
bz := k.cdc.MustMarshalBinaryBare(zeroCoin)
supplyStore.Set([]byte(coin.Denom), bz)
}
}
newSupply = append(newSupply, supply...)

store.Set(types.SupplyKey, bz)
for i := range newSupply {
bz := k.cdc.MustMarshalBinaryBare(&supply[i])
supplyStore.Set([]byte(supply[i].Denom), bz)
}
}

// GetDenomMetaData retrieves the denomination metadata
Expand Down Expand Up @@ -339,8 +370,8 @@ func (k BaseKeeper) MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
}

// update total supply
supply := k.GetSupply(ctx)
supply.Inflate(amt)
supply := k.GetTotalSupply(ctx)
supply = supply.Add(amt...)

k.setSupply(ctx, supply)

Expand Down Expand Up @@ -373,8 +404,9 @@ func (k BaseKeeper) BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins)
}

// update total supply
supply := k.GetSupply(ctx)
supply.Deflate(amt)
supply := k.GetTotalSupply(ctx)
supply = supply.Sub(amt)

k.setSupply(ctx, supply)

logger := k.Logger(ctx)
Expand Down Expand Up @@ -418,14 +450,19 @@ func (k BaseKeeper) trackUndelegation(ctx sdk.Context, addr sdk.AccAddress, amt
return nil
}

// MarshalSupply protobuf serializes a Supply interface
func (k BaseKeeper) MarshalSupply(supplyI exported.SupplyI) ([]byte, error) {
return k.cdc.MarshalInterface(supplyI)
}
func (k BaseViewKeeper) IterateTotalSupply(ctx sdk.Context, cb func(sdk.Coin) bool) {
store := ctx.KVStore(k.storeKey)
supplyStore := prefix.NewStore(store, types.SupplyKey)

iterator := supplyStore.Iterator(nil, nil)
defer iterator.Close()

// UnmarshalSupply returns a Supply interface from raw encoded supply
// bytes of a Proto-based Supply type
func (k BaseKeeper) UnmarshalSupply(bz []byte) (exported.SupplyI, error) {
var evi exported.SupplyI
return evi, k.cdc.UnmarshalInterface(bz, &evi)
for ; iterator.Valid(); iterator.Next() {
var balance sdk.Coin
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance)

if cb(balance) {
break
}
}
}
Loading

0 comments on commit eb8aaf9

Please sign in to comment.