Skip to content

Commit

Permalink
feat: Add convenience method for constructing key to access account's…
Browse files Browse the repository at this point in the history
… balance for a given denom (#12674)

This PR adds a convenience method for constructing the key necessary to query for the account's balance of a given denom.

I ran into this issue since we are using ABCI query now to perform balance requests because we are also requesting merkle proofs for the returned balance [here](https://github.com/celestiaorg/celestia-node/pull/911/files#diff-0ee31f5a7bd88e9f758e6bebdf3ee36365519e55a451098d9638c39afe5eac42R144).

It would be nice to have a definitive convenience method for constructing the key.

[Ref.](github.com/celestiaorg/celestia-node/pull/911)

(cherry picked from commit a1777a8)

# Conflicts:
#	CHANGELOG.md
#	x/bank/legacy/v043/store.go
#	x/bank/types/keys.go
  • Loading branch information
renaynay authored and mergify[bot] committed Jul 27, 2022
1 parent eb032e3 commit 606a04c
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 2 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,20 @@ Ref: https://keepachangelog.com/en/1.0.0/

* (x/params) [#12724](https://github.com/cosmos/cosmos-sdk/pull/12724) Add `GetParamSetIfExists` function to params `Subspace` to prevent panics on breaking changes.
* [#12668](https://github.com/cosmos/cosmos-sdk/pull/12668) Add `authz_msg_index` event attribute to message events emitted when executing via `MsgExec` through `x/authz`.
<<<<<<< HEAD
* [#12697](https://github.com/cosmos/cosmos-sdk/pull/12697) Upgrade IAVL to v0.19.0 with fast index and error propagation. NOTE: first start will take a while to propagate into new model.
=======
* [#12626](https://github.com/cosmos/cosmos-sdk/pull/12626) Upgrade IAVL to v0.19.0 with fast index and error propagation. NOTE: first start will take a while to propagate into new model.
* [#12649](https://github.com/cosmos/cosmos-sdk/pull/12649) Bump tendermint to v0.34.20.
* [#12576](https://github.com/cosmos/cosmos-sdk/pull/12576) Remove dependency on cosmos/keyring and upgrade to 99designs/keyring v1.2.1
* [#12590](https://github.com/cosmos/cosmos-sdk/pull/12590) Allow zero gas in simulation mode.
* [#12453](https://github.com/cosmos/cosmos-sdk/pull/12453) Add `NewInMemoryWithKeyring` function which allows the creation of in memory `keystore` instances with a specified set of existing items.
* [#11390](https://github.com/cosmos/cosmos-sdk/pull/11390) `LatestBlockResponse` & `BlockByHeightResponse` types' `Block` filed has been deprecated and they now contains new field `sdk_block` with `proposer_address` as `string`
* (deps) Downgrade to Tendermint [v0.34.20-rc0](https://github.com/tendermint/tendermint/releases/tag/v0.34.20-rc0).
* [#12089](https://github.com/cosmos/cosmos-sdk/pull/12089) Mark the `TipDecorator` as beta, don't include it in simapp by default.
* [#12153](https://github.com/cosmos/cosmos-sdk/pull/12153) Add a new `NewSimulationManagerFromAppModules` constructor, to simplify simulation wiring.
* (x/bank) [#12674](https://github.com/cosmos/cosmos-sdk/pull/12674) Add convenience function `CreatePrefixedAccountStoreKey()` to construct key to access account's balance for a given denom.
>>>>>>> a1777a87b (feat: Add convenience method for constructing key to access account's balance for a given denom (#12674))
### Bug Fixes

Expand Down
6 changes: 6 additions & 0 deletions x/bank/legacy/v043/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@ func migrateBalanceKeys(store sdk.KVStore) {
defer oldStoreIter.Close()

for ; oldStoreIter.Valid(); oldStoreIter.Next() {
<<<<<<< HEAD:x/bank/legacy/v043/store.go
addr := v040bank.AddressFromBalancesStore(oldStoreIter.Key())
denom := oldStoreIter.Key()[v040auth.AddrLen:]
newStoreKey := append(types.CreateAccountBalancesPrefix(addr), denom...)
=======
addr := v1.AddressFromBalancesStore(oldStoreIter.Key())
denom := oldStoreIter.Key()[v042auth.AddrLen:]
newStoreKey := types.CreatePrefixedAccountStoreKey(addr, denom)
>>>>>>> a1777a87b (feat: Add convenience method for constructing key to access account's balance for a given denom (#12674)):x/bank/migrations/v2/store.go

// Set new key on store. Values don't change.
store.Set(newStoreKey, oldStoreIter.Value())
Expand Down
4 changes: 2 additions & 2 deletions x/bank/legacy/v043/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ func TestBalanceKeysMigration(t *testing.T) {
err = v043bank.MigrateStore(ctx, bankKey, encCfg.Marshaler)
require.NoError(t, err)

newKey := append(types.CreateAccountBalancesPrefix(addr), []byte(fooCoin.Denom)...)
newKey := types.CreatePrefixedAccountStoreKey(addr, []byte(fooCoin.Denom))
// -7 because we replaced "balances" with 0x02,
// +1 because we added length-prefix to address.
require.Equal(t, len(oldFooKey)-7+1, len(newKey))
require.Nil(t, store.Get(oldFooKey))
require.Equal(t, fooBz, store.Get(newKey))

newKeyFooBar := append(types.CreateAccountBalancesPrefix(addr), []byte(fooBarCoin.Denom)...)
newKeyFooBar := types.CreatePrefixedAccountStoreKey(addr, []byte(fooBarCoin.Denom))
require.Nil(t, store.Get(newKeyFooBar)) // after migration zero balances pruned from store.
}
112 changes: 112 additions & 0 deletions x/bank/types/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package types

import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/address"
"github.com/cosmos/cosmos-sdk/types/kv"
)

const (
// ModuleName defines the module name
ModuleName = "bank"

// StoreKey defines the primary module store key
StoreKey = ModuleName

// RouterKey defines the module's message routing key
RouterKey = ModuleName

// QuerierRoute defines the module's query routing key
QuerierRoute = ModuleName

// ModuleQueryPath defines the ABCI query path of the module
ModuleQueryPath = "store/bank/key"
)

// KVStore keys
var (
SupplyKey = []byte{0x00}
DenomMetadataPrefix = []byte{0x1}
DenomAddressPrefix = []byte{0x03}

// BalancesPrefix is the prefix for the account balances store. We use a byte
// (instead of `[]byte("balances")` to save some disk space).
BalancesPrefix = []byte{0x02}

// SendEnabledPrefix is the prefix for the SendDisabled flags for a Denom.
SendEnabledPrefix = []byte{0x04}

// ParamsKey is the prefix for x/bank parameters
ParamsKey = []byte{0x05}
)

const (
// TrueB is a byte with value 1 that represents true.
TrueB = byte(0x01)
// FalseB is a byte with value 0 that represents false.
FalseB = byte(0x00)
)

// AddressAndDenomFromBalancesStore returns an account address and denom from a balances prefix
// store. The key must not contain the prefix BalancesPrefix as the prefix store
// iterator discards the actual prefix.
//
// If invalid key is passed, AddressAndDenomFromBalancesStore returns ErrInvalidKey.
func AddressAndDenomFromBalancesStore(key []byte) (sdk.AccAddress, string, error) {
if len(key) == 0 {
return nil, "", ErrInvalidKey
}

kv.AssertKeyAtLeastLength(key, 1)

addrBound := int(key[0])

if len(key)-1 < addrBound {
return nil, "", ErrInvalidKey
}

return key[1 : addrBound+1], string(key[addrBound+1:]), nil
}

// CreatePrefixedAccountStoreKey returns the key for the given account and denomination.
// This method can be used when performing an ABCI query for the balance of an account.
func CreatePrefixedAccountStoreKey(addr []byte, denom []byte) []byte {
return append(CreateAccountBalancesPrefix(addr), denom...)
}

// CreateAccountBalancesPrefix creates the prefix for an account's balances.
func CreateAccountBalancesPrefix(addr []byte) []byte {
return append(BalancesPrefix, address.MustLengthPrefix(addr)...)
}

// CreateDenomAddressPrefix creates a prefix for a reverse index of denomination
// to account balance for that denomination.
func CreateDenomAddressPrefix(denom string) []byte {
// we add a "zero" byte at the end - null byte terminator, to allow prefix denom prefix
// scan. Setting it is not needed (key[last] = 0) - because this is the default.
key := make([]byte, len(DenomAddressPrefix)+len(denom)+1)
copy(key, DenomAddressPrefix)
copy(key[len(DenomAddressPrefix):], denom)
return key
}

// CreateSendEnabledKey creates the key of the SendDisabled flag for a denom.
func CreateSendEnabledKey(denom string) []byte {
key := make([]byte, len(SendEnabledPrefix)+len(denom))
copy(key, SendEnabledPrefix)
copy(key[len(SendEnabledPrefix):], denom)
return key
}

// IsTrueB returns true if the provided byte slice has exactly one byte, and it is equal to TrueB.
func IsTrueB(bz []byte) bool {
return len(bz) == 1 && bz[0] == TrueB
}

// ToBoolB returns TrueB if v is true, and FalseB if it's false.
func ToBoolB(v bool) byte {
if v {
return TrueB
}
return FalseB
}

0 comments on commit 606a04c

Please sign in to comment.