Skip to content

Commit

Permalink
Implement ADR 024 - Coin Metadata (cosmos#6821)
Browse files Browse the repository at this point in the history
* Implement ADR 024 - Coin Metadata

* Fix lint

* Fix proto lint

* Index metadata by denom

* Fix test

* Fix proto comments

* Add GetAllDenomMetaData help method and refactor code accordingly

* Add x/bank/keeper/genesis_test.go

Co-authored-by: Alexander Bezobchuk <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 28, 2020
1 parent 5676e72 commit 5a7e220
Show file tree
Hide file tree
Showing 14 changed files with 1,805 additions and 715 deletions.
17 changes: 17 additions & 0 deletions proto/cosmos/bank/bank.proto
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,20 @@ message Supply {
repeated cosmos.Coin total = 1
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
}

// DenomUnits represents a struct that describes different
// denominations units of the basic token
message DenomUnits {
string denom = 1;
uint32 exponent = 2;
repeated string aliases = 3;
}

// Metadata represents a struct that describes
// a basic token
message Metadata {
string description = 1;
repeated DenomUnits denom_units = 2;
string base = 3;
string display = 4;
}
4 changes: 2 additions & 2 deletions simapp/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs
}

// update total supply
bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply)
bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{})
genesisState[banktypes.ModuleName] = app.Codec().MustMarshalJSON(bankGenesis)

stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
Expand Down Expand Up @@ -158,7 +158,7 @@ func SetupWithGenesisAccounts(genAccs []authtypes.GenesisAccount, balances ...ba
totalSupply = totalSupply.Add(b.Coins...)
}

bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply)
bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{})
genesisState[banktypes.ModuleName] = app.Codec().MustMarshalJSON(bankGenesis)

stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
Expand Down
212 changes: 212 additions & 0 deletions tests/fixtures/adr-024-coin-metadata_genesis.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
{
"genesis_time": "2020-07-18T22:22:09.286222793Z",
"chain_id": "123",
"consensus_params": {
"block": {
"max_bytes": "22020096",
"max_gas": "-1",
"time_iota_ms": "1000"
},
"evidence": {
"max_age_num_blocks": "100000",
"max_age_duration": "172800000000000"
},
"validator": {
"pub_key_types": [
"ed25519"
]
}
},
"app_hash": "",
"app_state": {
"bank": {
"params": {
"default_send_enabled": true
},
"balances": [ {"address": "cosmos106vrzv5xkheqhjm023pxcxlqmcjvuhtfyachz4",
"coins": [
{
"denom": "nametoken",
"amount": "1000"
},
{
"denom": "stake",
"amount": "100000000"
}
]
},
{ "address": "cosmos1xztun2634zplhajda7tmjaxy488qj44n765t58",
"coins": [
{
"denom": "nametoken",
"amount": "1000"
},
{
"denom": "stake",
"amount": "100000000"
}
]
}
],

"supply": [],
"denom_metadata": [{
"description": "The native staking token of the Cosmos Hub.",
"denom_units": [ {
"denom": "uatom",
"exponent": 0,
"aliases": [
"microatom"
]
},
{
"denom": "matom",
"exponent": 3,
"aliases": [
"milliatom"
]
},
{
"denom": "atom",
"exponent": 6
}],
"base": "uatom",
"display": "atom"
}]

},
"genutil": {
"gentxs": []
},
"capability": {
"index": "1",
"owners": []
},
"mint": {
"minter": {
"inflation": "0.130000000000000000",
"annual_provisions": "0.000000000000000000"
},
"params": {
"mint_denom": "stake",
"inflation_rate_change": "0.130000000000000000",
"inflation_max": "0.200000000000000000",
"inflation_min": "0.070000000000000000",
"goal_bonded": "0.670000000000000000",
"blocks_per_year": "6311520"
}
},
"ibc": {
"client_genesis": {
"clients": [],
"clients_consensus": [],
"create_localhost": true
},
"connection_genesis": {
"connections": [],
"client_connection_paths": []
},
"channel_genesis": {
"channels": [],
"acknowledgements": [],
"commitments": [],
"send_sequences": [],
"recv_sequences": [],
"ack_sequences": []
}
},
"upgrade": {},
"evidence": {
"evidence": []
},
"auth": {
"params": {
"max_memo_characters": "256",
"tx_sig_limit": "7",
"tx_size_cost_per_byte": "10",
"sig_verify_cost_ed25519": "590",
"sig_verify_cost_secp256k1": "1000"
},
"accounts": []
},
"gov": {
"starting_proposal_id": "1",
"deposits": null,
"votes": null,
"proposals": null,
"deposit_params": {
"min_deposit": [
{
"denom": "stake",
"amount": "10000000"
}
],
"max_deposit_period": "172800000000000"
},
"voting_params": {
"voting_period": "172800000000000"
},
"tally_params": {
"quorum": "0.334000000000000000",
"threshold": "0.500000000000000000",
"veto": "0.334000000000000000"
}
},
"params": null,
"transfer": {
"port_id": "transfer"
},
"crisis": {
"constant_fee": {
"denom": "stake",
"amount": "1000"
}
},
"distribution": {
"params": {
"community_tax": "0.020000000000000000",
"base_proposer_reward": "0.010000000000000000",
"bonus_proposer_reward": "0.040000000000000000",
"withdraw_addr_enabled": true
},
"fee_pool": {
"community_pool": []
},
"delegator_withdraw_infos": [],
"previous_proposer": "",
"outstanding_rewards": [],
"validator_accumulated_commissions": [],
"validator_historical_rewards": [],
"validator_current_rewards": [],
"delegator_starting_infos": [],
"validator_slash_events": []
},
"slashing": {
"params": {
"signed_blocks_window": "100",
"min_signed_per_window": "0.500000000000000000",
"downtime_jail_duration": "600000000000",
"slash_fraction_double_sign": "0.050000000000000000",
"slash_fraction_downtime": "0.010000000000000000"
},
"signing_infos": {},
"missed_blocks": {}
},
"staking": {
"params": {
"unbonding_time": "1814400000000000",
"max_validators": 100,
"max_entries": 7,
"historical_entries": 100,
"bond_denom": "stake"
},
"last_total_power": "0",
"last_validator_powers": null,
"validators": null,
"delegations": null,
"unbonding_delegations": null,
"redelegations": null,
"exported": false
}
}
}
2 changes: 1 addition & 1 deletion x/bank/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ func (k BaseKeeper) ExportGenesis(ctx sdk.Context) types.GenesisState {
})
}

return types.NewGenesisState(k.GetParams(ctx), balances, k.GetSupply(ctx).GetTotal())
return types.NewGenesisState(k.GetParams(ctx), balances, k.GetSupply(ctx).GetTotal(), k.GetAllDenomMetaData(ctx))
}
40 changes: 40 additions & 0 deletions x/bank/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package keeper_test

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank/types"
)

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

expectedMetadata := suite.getTestMetadata()
expectedBalances := suite.getTestBalances()
for i := range []int{1, 2} {
app.BankKeeper.SetDenomMetaData(ctx, expectedMetadata[i])
err := app.BankKeeper.SetBalances(ctx, expectedBalances[i].Address, expectedBalances[i].Coins)
suite.Require().NoError(err)
}

totalSupply := types.NewSupply(sdk.NewCoins(sdk.NewInt64Coin("test", 400000000)))
app.BankKeeper.SetSupply(ctx, totalSupply)
app.BankKeeper.SetParams(ctx, types.DefaultParams())

exportGenesis := app.BankKeeper.ExportGenesis(ctx)

suite.Require().Len(exportGenesis.Params.SendEnabled, 0)
suite.Require().Equal(types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled)
suite.Require().Equal(totalSupply.GetTotal(), exportGenesis.Supply)
suite.Require().Equal(expectedBalances, exportGenesis.Balances)
suite.Require().Equal(expectedMetadata, exportGenesis.DenomMetadata)
}

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

}
58 changes: 58 additions & 0 deletions x/bank/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
Expand All @@ -27,6 +28,10 @@ type Keeper interface {
GetSupply(ctx sdk.Context) exported.SupplyI
SetSupply(ctx sdk.Context, supply exported.SupplyI)

GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata
SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata)
IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool)

SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
SendCoinsFromModuleToModule(ctx sdk.Context, senderModule, recipientModule string, amt sdk.Coins) error
SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
Expand Down Expand Up @@ -175,6 +180,59 @@ func (k BaseKeeper) SetSupply(ctx sdk.Context, supply exported.SupplyI) {
store.Set(types.SupplyKey, bz)
}

// GetDenomMetaData retrieves the denomination metadata
func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) types.Metadata {
store := ctx.KVStore(k.storeKey)
store = prefix.NewStore(store, types.DenomMetadataKey(denom))

bz := store.Get([]byte(denom))

var metadata types.Metadata
k.cdc.MustUnmarshalBinaryBare(bz, &metadata)

return metadata
}

// GetAllDenomMetaData retrieves all denominations metadata
func (k BaseKeeper) GetAllDenomMetaData(ctx sdk.Context) []types.Metadata {
denomMetaData := make([]types.Metadata, 0)
k.IterateAllDenomMetaData(ctx, func(metadata types.Metadata) bool {
denomMetaData = append(denomMetaData, metadata)
return false
})

return denomMetaData
}

// IterateAllDenomMetaData iterates over all the denominations metadata and
// provides the metadata to a callback. If true is returned from the
// callback, iteration is halted.
func (k BaseKeeper) IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metadata) bool) {
store := ctx.KVStore(k.storeKey)
denomMetaDataStore := prefix.NewStore(store, types.DenomMetadataPrefix)

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

for ; iterator.Valid(); iterator.Next() {
var metadata types.Metadata
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &metadata)

if cb(metadata) {
break
}
}
}

// SetDenomMetaData sets the denominations metadata
func (k BaseKeeper) SetDenomMetaData(ctx sdk.Context, denomMetaData types.Metadata) {
store := ctx.KVStore(k.storeKey)
denomMetaDataStore := prefix.NewStore(store, types.DenomMetadataKey(denomMetaData.Base))

m := k.cdc.MustMarshalBinaryBare(&denomMetaData)
denomMetaDataStore.Set([]byte(denomMetaData.Base), m)
}

// SendCoinsFromModuleToAccount transfers coins from a ModuleAccount to an AccAddress.
// It will panic if the module account does not exist.
func (k BaseKeeper) SendCoinsFromModuleToAccount(
Expand Down
Loading

0 comments on commit 5a7e220

Please sign in to comment.