From 5081dc1928ae7d33524c4cce2f5d659ed3e65617 Mon Sep 17 00:00:00 2001 From: Denys S <150304777+dssei@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:17:32 -0700 Subject: [PATCH] Token allow list feature. Bank module changes (#538) ## Describe your changes and provide context This change is part of token allow list feature implementation for token extensions. Added functions to: - persist denom allow list and query it - check if `from` and `to` addresses are in allowlist on bank send and multi-send ## Testing performed to validate your change - unit testing - functional testing (plugged modified code into sei-chain) and via ERC-20 pointer contract --- proto/cosmos/bank/v1beta1/bank.proto | 7 + x/bank/keeper/keeper_test.go | 389 ++++++++++++++++++++++++++- x/bank/keeper/send.go | 132 +++++++++ x/bank/types/bank.pb.go | 283 ++++++++++++++++--- x/bank/types/key.go | 15 +- 5 files changed, 778 insertions(+), 48 deletions(-) diff --git a/proto/cosmos/bank/v1beta1/bank.proto b/proto/cosmos/bank/v1beta1/bank.proto index df91008df..1682f3d13 100644 --- a/proto/cosmos/bank/v1beta1/bank.proto +++ b/proto/cosmos/bank/v1beta1/bank.proto @@ -94,3 +94,10 @@ message Metadata { // Since: cosmos-sdk 0.43 string symbol = 6; } + +message AllowList { + option (gogoproto.equal) = true; + + // Can be empty for no admin, or a valid sei address + repeated string addresses = 1 [ (gogoproto.moretags) = "yaml:\"addresses\"" ]; +} diff --git a/x/bank/keeper/keeper_test.go b/x/bank/keeper/keeper_test.go index 67dbbca78..ea6a77ea3 100644 --- a/x/bank/keeper/keeper_test.go +++ b/x/bank/keeper/keeper_test.go @@ -23,12 +23,13 @@ import ( ) const ( - fooDenom = "foo" - barDenom = "bar" - initialPower = int64(100) - holder = "holder" - multiPerm = "multiple permissions account" - randomPerm = "random permission" + fooDenom = "foo" + barDenom = "bar" + factoryDenomPrefix = "factory" + initialPower = int64(100) + holder = "holder" + multiPerm = "multiple permissions account" + randomPerm = "random permission" ) var ( @@ -47,6 +48,14 @@ func newFooCoin(amt int64) sdk.Coin { return sdk.NewInt64Coin(fooDenom, amt) } +func newFactoryFooCoin(address sdk.AccAddress, amt int64) sdk.Coin { + return sdk.NewInt64Coin(fmt.Sprintf("%s/%s/%s", factoryDenomPrefix, address, fooDenom), amt) +} + +func newFactoryBarCoin(address sdk.AccAddress, amt int64) sdk.Coin { + return sdk.NewInt64Coin(fmt.Sprintf("%s/%s/%s", factoryDenomPrefix, address, barDenom), amt) +} + func newBarCoin(amt int64) sdk.Coin { return sdk.NewInt64Coin(barDenom, amt) } @@ -485,6 +494,108 @@ func (suite *IntegrationTestSuite) TestInputOutputCoins() { suite.Require().Equal(expected, acc3Balances) } +func (suite *IntegrationTestSuite) TestInputOutputCoinsWithAllowList() { + app, ctx := suite.app, suite.ctx + + addr1 := sdk.AccAddress("addr1_______________") + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + factoryCoin := newFactoryFooCoin(addr1, 100) + balances := sdk.NewCoins(factoryCoin) + + addr2 := sdk.AccAddress("addr2_______________") + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + + addr3 := sdk.AccAddress("addr3_______________") + acc3 := app.AccountKeeper.NewAccountWithAddress(ctx, addr3) + app.AccountKeeper.SetAccount(ctx, acc3) + + addr4 := sdk.AccAddress("addr4_______________") + acc4 := app.AccountKeeper.NewAccountWithAddress(ctx, addr4) + app.AccountKeeper.SetAccount(ctx, acc4) + + suite.Require().NoError(simapp.FundAccount(app.BankKeeper, ctx, addr1, balances)) + app.BankKeeper.SetDenomAllowList(ctx, factoryCoin.Denom, + types.AllowList{ + Addresses: []string{addr1.String(), addr2.String(), addr3.String()}}) + + inputs := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 30))}, + {Address: addr1.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 30))}, + } + outputs := []types.Output{ + {Address: addr2.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 30))}, + {Address: addr3.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 30))}, + } + suite.Require().NoError(app.BankKeeper.InputOutputCoins(ctx, inputs, outputs)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFactoryFooCoin(addr1, 40)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc2Balances) + + acc3Balances := app.BankKeeper.GetAllBalances(ctx, addr3) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc3Balances) + + inputs1 := []types.Input{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 5))}, + {Address: addr1.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 10))}, + } + outputs1 := []types.Output{ + {Address: addr2.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 5))}, + {Address: addr4.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 10))}, + } + + err := app.BankKeeper.InputOutputCoins(ctx, inputs1, outputs1) + suite.Require().Error(err) + suite.Require().Equal( + fmt.Sprintf("%s is not allowed to receive funds: unauthorized", addr4.String()), err.Error()) + + acc1Balances = app.BankKeeper.GetAllBalances(ctx, addr1) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 40)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances = app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc2Balances) + + acc3Balances = app.BankKeeper.GetAllBalances(ctx, addr3) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc3Balances) + + inputs2 := []types.Input{ + {Address: addr4.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 5))}, + {Address: addr4.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 10))}, + } + outputs2 := []types.Output{ + {Address: addr1.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 5))}, + {Address: addr2.String(), Coins: sdk.NewCoins(newFactoryFooCoin(addr1, 10))}, + } + + err = app.BankKeeper.InputOutputCoins(ctx, inputs2, outputs2) + suite.Require().Error(err) + suite.Require().Equal( + fmt.Sprintf("%s is not allowed to send funds: unauthorized", addr4.String()), err.Error()) + + acc1Balances = app.BankKeeper.GetAllBalances(ctx, addr1) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 40)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances = app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc2Balances) + + acc3Balances = app.BankKeeper.GetAllBalances(ctx, addr3) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 30)) + suite.Require().Equal(expected, acc3Balances) + +} + func (suite *IntegrationTestSuite) TestSendCoins() { app, ctx := suite.app, suite.ctx balances := sdk.NewCoins(newFooCoin(100), newBarCoin(50)) @@ -522,6 +633,98 @@ func (suite *IntegrationTestSuite) TestSendCoins() { suite.Require().Equal(newBarCoin(25), coins[0], "expected only bar coins in the account balance, got: %v", coins) } +func (suite *IntegrationTestSuite) TestSendCoinsWithAllowList() { + app, ctx := suite.app, suite.ctx + addr1 := sdk.AccAddress("addr1_______________") + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + factoryCoin := newFactoryFooCoin(addr1, 100) + balances := sdk.NewCoins(factoryCoin, newBarCoin(50)) + suite.Require().NoError(simapp.FundAccount(app.BankKeeper, ctx, addr1, balances)) + + addr2 := sdk.AccAddress("addr2_______________") + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + + app.BankKeeper.SetDenomAllowList(ctx, factoryCoin.Denom, + types.AllowList{Addresses: []string{addr1.String(), addr2.String()}}) + + sendAmt := sdk.NewCoins(newFactoryFooCoin(addr1, 40), newBarCoin(25)) + suite.Require().NoError(app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt)) + + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFactoryFooCoin(addr1, 60), newBarCoin(25)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 40), newBarCoin(25)) + suite.Require().Equal(expected, acc2Balances) +} + +func (suite *IntegrationTestSuite) TestSendCoinsWithSenderNotInAllowList() { + app, ctx := suite.app, suite.ctx + addr1 := sdk.AccAddress("addr1_______________") + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + factoryCoin := newFactoryFooCoin(addr1, 100) + balances := sdk.NewCoins(factoryCoin, newBarCoin(50)) + suite.Require().NoError(simapp.FundAccount(app.BankKeeper, ctx, addr1, balances)) + + addr2 := sdk.AccAddress("addr2_______________") + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + + app.BankKeeper.SetDenomAllowList(ctx, factoryCoin.Denom, + types.AllowList{Addresses: []string{addr2.String()}}) + + sendAmt := sdk.NewCoins(newFactoryFooCoin(addr1, 5), newBarCoin(5)) + err := app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt) + suite.Require().Error(err) + suite.Require().Equal( + fmt.Sprintf("%s is not allowed to send funds: unauthorized", addr1.String()), err.Error()) + + // Balances should remain the same + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFactoryFooCoin(addr1, 100), newBarCoin(50)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 0), newBarCoin(0)) + suite.Require().Equal(expected, acc2Balances) +} + +func (suite *IntegrationTestSuite) TestSendCoinsWithReceiverNotInAllowList() { + app, ctx := suite.app, suite.ctx + addr1 := sdk.AccAddress("addr1_______________") + acc1 := app.AccountKeeper.NewAccountWithAddress(ctx, addr1) + app.AccountKeeper.SetAccount(ctx, acc1) + factoryCoin := newFactoryFooCoin(addr1, 100) + balances := sdk.NewCoins(factoryCoin, newBarCoin(50)) + suite.Require().NoError(simapp.FundAccount(app.BankKeeper, ctx, addr1, balances)) + + addr2 := sdk.AccAddress("addr2_______________") + acc2 := app.AccountKeeper.NewAccountWithAddress(ctx, addr2) + app.AccountKeeper.SetAccount(ctx, acc2) + + app.BankKeeper.SetDenomAllowList(ctx, factoryCoin.Denom, + types.AllowList{Addresses: []string{addr1.String()}}) + + sendAmt := sdk.NewCoins(newFactoryFooCoin(addr1, 5), newBarCoin(5)) + err := app.BankKeeper.SendCoins(ctx, addr1, addr2, sendAmt) + suite.Require().Error(err) + suite.Require().Equal( + fmt.Sprintf("%s is not allowed to receive funds: unauthorized", addr2.String()), err.Error()) + + // Balances should remain the same + acc1Balances := app.BankKeeper.GetAllBalances(ctx, addr1) + expected := sdk.NewCoins(newFactoryFooCoin(addr1, 100), newBarCoin(50)) + suite.Require().Equal(expected, acc1Balances) + + acc2Balances := app.BankKeeper.GetAllBalances(ctx, addr2) + expected = sdk.NewCoins(newFactoryFooCoin(addr1, 0), newBarCoin(0)) + suite.Require().Equal(expected, acc2Balances) +} + func (suite *IntegrationTestSuite) TestSendCoinsModuleToAccount() { // add module accounts to supply keeper ctx := suite.ctx @@ -1361,6 +1564,180 @@ func (suite *IntegrationTestSuite) TestSetDenomMetaData() { suite.Require().Equal(metadata[1].GetDenomUnits()[1].GetAliases(), actualMetadata.GetDenomUnits()[1].GetAliases()) } +func (suite *IntegrationTestSuite) TestSetAllowList() { + app, ctx := suite.app, suite.ctx + + allowList := types.AllowList{Addresses: []string{"addr1", "addr2"}} + ownerAddress := sdk.AccAddress("owner") + denom := "factory/" + ownerAddress.String() + "/Test" + + app.BankKeeper.SetDenomAllowList(ctx, denom, allowList) + + actualAllowList := app.BankKeeper.GetDenomAllowList(ctx, denom) + suite.Require().Equal(allowList, actualAllowList) +} + +func (suite *IntegrationTestSuite) TestBaseKeeper_IsAllowedToSendCoins() { + type CoinToAllowList struct { + coin sdk.Coin + allowList types.AllowList + } + type args struct { + addr sdk.AccAddress + coinsToAllowList []CoinToAllowList + } + tests := []struct { + name string + args args + isAllowed bool + }{ + { + name: "Allowed for for empty coins", + args: args{ + addr: sdk.AccAddress{}, + coinsToAllowList: []CoinToAllowList{ + {coin: sdk.Coin{}}, + }, + }, + isAllowed: true, + }, + { + name: "allowed for to transfer with a non-factory coin", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin("test", 100), + }, + }, + }, + isAllowed: true, + }, + { + name: "allowed with a factory coin with no allow list", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin( + fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + }, + }, + }, + isAllowed: true, + }, + { + name: "allowed with a factory coin with empty allow list", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin( + fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + allowList: types.AllowList{}, + }, + }, + }, + isAllowed: true, + }, + { + name: "not allowed to transfer coins for denom if not in allowlist", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("other").String()}, + }, + }, + }, + }, + isAllowed: false, + }, + { + name: "allowed to transfer for denom", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("from").String(), sdk.AccAddress("to").String()}, + }, + }, + }, + }, + isAllowed: true, + }, + { + name: "allowed for one coin but not allowed for another coin", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("from").String(), sdk.AccAddress("to").String()}, + }, + }, + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test2", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("other").String(), sdk.AccAddress("yetanother").String()}, + }, + }, + }, + }, + isAllowed: false, + }, + { + name: "not allowed for first coin but allowed for another coin", + args: args{ + addr: sdk.AccAddress("from"), + coinsToAllowList: []CoinToAllowList{ + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("other").String(), sdk.AccAddress("yetanother").String()}, + }, + }, + { + coin: sdk.NewInt64Coin(fmt.Sprintf("factory/%s/test2", sdk.AccAddress("from")), 100), + allowList: types.AllowList{ + Addresses: []string{sdk.AccAddress("from").String(), sdk.AccAddress("to").String()}, + }, + }, + }, + }, + isAllowed: false, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + app, ctx := suite.app, suite.ctx + coins := sdk.NewCoins() + for _, coinToAllowList := range tt.args.coinsToAllowList { + if coinToAllowList.coin.Denom != "" { + coins = coins.Add(coinToAllowList.coin) + if coinToAllowList.allowList.Addresses != nil { + app.BankKeeper.SetDenomAllowList(ctx, coinToAllowList.coin.Denom, coinToAllowList.allowList) + } + } + } + denomToAllowedAddressesCache := make(map[string]keeper.AllowedAddresses) + isAllowed := + app.BankKeeper.IsAllowedToSendCoins(ctx, tt.args.addr, coins, denomToAllowedAddressesCache) + + // Use suite.Require to assert the results + suite.Require().Equal(tt.isAllowed, isAllowed, + fmt.Errorf("IsAllowedToSendCoins() isAllowed = %v, want %v", + isAllowed, tt.isAllowed)) + }) + } +} + func (suite *IntegrationTestSuite) TestCanSendTo() { app, ctx := suite.app, suite.ctx badAddr := sdk.AccAddress([]byte("addr1_______________")) diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index bfc7ea9d5..d62829afb 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -1,6 +1,8 @@ package keeper import ( + "strings" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" "github.com/cosmos/cosmos-sdk/telemetry" @@ -10,6 +12,11 @@ import ( paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" ) +const ( + TokenFactoryPrefix = "factory" + TokenFactoryModuleName = "tokenfactory" +) + // SendKeeper defines a module interface that facilitates the transfer of coins // between accounts without the possibility of creating coins. type SendKeeper interface { @@ -29,6 +36,9 @@ type SendKeeper interface { IsSendEnabledCoin(ctx sdk.Context, coin sdk.Coin) bool IsSendEnabledCoins(ctx sdk.Context, coins ...sdk.Coin) error + SetDenomAllowList(ctx sdk.Context, denom string, allowList types.AllowList) + GetDenomAllowList(ctx sdk.Context, denom string) types.AllowList + IsAllowedToSendCoins(ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins, cache map[string]AllowedAddresses) bool BlockedAddr(addr sdk.AccAddress) bool RegisterRecipientChecker(RecipientChecker) @@ -90,6 +100,11 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, return err } + err := k.validateInputOutputAddressesAllowedToSendCoins(ctx, inputs, outputs) + if err != nil { + return err + } + for _, in := range inputs { inAddress, err := sdk.AccAddressFromBech32(in.Address) if err != nil { @@ -141,9 +156,45 @@ func (k BaseSendKeeper) InputOutputCoins(ctx sdk.Context, inputs []types.Input, return nil } +func (k BaseSendKeeper) validateInputOutputAddressesAllowedToSendCoins(ctx sdk.Context, inputs []types.Input, outputs []types.Output) error { + denomToAllowListCache := make(map[string]AllowedAddresses) + for _, in := range inputs { + inAddress, err := sdk.AccAddressFromBech32(in.Address) + if err != nil { + return err + } + allowedToSend := k.IsAllowedToSendCoins(ctx, inAddress, in.Coins, denomToAllowListCache) + if !allowedToSend { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to send funds", inAddress) + } + } + for _, out := range outputs { + outAddress, err := sdk.AccAddressFromBech32(out.Address) + if err != nil { + return err + } + allowedToReceive := k.IsAllowedToSendCoins(ctx, outAddress, out.Coins, denomToAllowListCache) + if !allowedToReceive { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", outAddress) + } + } + return nil +} + // SendCoins transfers amt coins from a sending account to a receiving account. // An error is returned upon failure. func (k BaseSendKeeper) SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { + denomToAllowedAddressesCache := make(map[string]AllowedAddresses) + fromAllowed := k.IsAllowedToSendCoins(ctx, fromAddr, amt, denomToAllowedAddressesCache) + if !fromAllowed { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to send funds", fromAddr) + } + + toAllowed := k.IsAllowedToSendCoins(ctx, toAddr, amt, denomToAllowedAddressesCache) + if !toAllowed { + return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", toAddr) + } + if err := k.SendCoinsWithoutAccCreation(ctx, fromAddr, toAddr, amt); err != nil { return err } @@ -432,3 +483,84 @@ func (k BaseSendKeeper) CanSendTo(ctx sdk.Context, recipient sdk.AccAddress) boo func SplitUseiWeiAmount(amt sdk.Int) (sdk.Int, sdk.Int) { return amt.Quo(OneUseiInWei), amt.Mod(OneUseiInWei) } + +func (k BaseSendKeeper) SetDenomAllowList(ctx sdk.Context, denom string, allowList types.AllowList) { + store := ctx.KVStore(k.storeKey) + denomAllowListStore := prefix.NewStore(store, types.DenomAllowListKey(denom)) + + m := k.cdc.MustMarshal(&allowList) + denomAllowListStore.Set([]byte(denom), m) +} + +func (k BaseSendKeeper) GetDenomAllowList(ctx sdk.Context, denom string) types.AllowList { + store := ctx.KVStore(k.storeKey) + store = prefix.NewStore(store, types.DenomAllowListKey(denom)) + + bz := store.Get([]byte(denom)) + if bz == nil { + return types.AllowList{} + } + + var allowList types.AllowList + k.cdc.MustUnmarshal(bz, &allowList) + + return allowList +} + +// IsAllowedToSendCoins checks if the given address is allowed to send the given coins. +// The check is performed only fot token factory denoms. For each token factory denom, +// it checks if there is allow list for the given denom. If there is no allow list, +// the address is allowed to send the coins. If there is an allow list, the address is +// allowed to send the coins only if it is in the allow list. +func (k BaseSendKeeper) IsAllowedToSendCoins(ctx sdk.Context, addr sdk.AccAddress, coins sdk.Coins, cache map[string]AllowedAddresses) bool { + for _, coin := range coins { + // process only if denom does contain token factory prefix + if strings.HasPrefix(coin.Denom, TokenFactoryPrefix) { + allowedAddresses := k.getAllowedAddresses(ctx, cache, coin.Denom) + if len(allowedAddresses.set) > 0 { + // Add token factory module address to allowlist for minting tokens if allowlist is not empty + tokenFactoryAddr := k.ak.GetModuleAddress(TokenFactoryModuleName) + if tokenFactoryAddr != nil { + allowedAddresses.set[tokenFactoryAddr.String()] = struct{}{} + } + + if !allowedAddresses.contains(addr) { + return false + } + } + } + } + return true +} + +func (k BaseSendKeeper) getAllowedAddresses(ctx sdk.Context, cache map[string]AllowedAddresses, denom string) AllowedAddresses { + allowedAddresses, exists := cache[denom] + if !exists { + allowList := k.GetDenomAllowList(ctx, denom) + allowedAddresses = k.buildAllowedAddressesMap(allowList) + // we cache even if the allowList is empty to avoid multiple db reads + cache[denom] = allowedAddresses + } + return allowedAddresses +} + +type AllowedAddresses struct { + set map[string]struct{} +} + +func (a AllowedAddresses) contains(address sdk.AccAddress) bool { + _, exists := a.set[address.String()] + return exists +} + +func (k BaseSendKeeper) buildAllowedAddressesMap(allowList types.AllowList) AllowedAddresses { + allowedAddressesMap := make(map[string]struct{}) + if allowList.Addresses != nil && len(allowList.Addresses) > 0 { + for _, addr := range allowList.Addresses { + allowedAddressesMap[addr] = struct{}{} + } + } + return AllowedAddresses{ + set: allowedAddressesMap, + } +} diff --git a/x/bank/types/bank.pb.go b/x/bank/types/bank.pb.go index 8557be66e..180339f5f 100644 --- a/x/bank/types/bank.pb.go +++ b/x/bank/types/bank.pb.go @@ -417,6 +417,51 @@ func (m *Metadata) GetSymbol() string { return "" } +type AllowList struct { + // Can be empty for no admin, or a valid sei address + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty" yaml:"addresses"` +} + +func (m *AllowList) Reset() { *m = AllowList{} } +func (m *AllowList) String() string { return proto.CompactTextString(m) } +func (*AllowList) ProtoMessage() {} +func (*AllowList) Descriptor() ([]byte, []int) { + return fileDescriptor_dd052eee12edf988, []int{7} +} +func (m *AllowList) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AllowList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AllowList.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AllowList) XXX_Merge(src proto.Message) { + xxx_messageInfo_AllowList.Merge(m, src) +} +func (m *AllowList) XXX_Size() int { + return m.Size() +} +func (m *AllowList) XXX_DiscardUnknown() { + xxx_messageInfo_AllowList.DiscardUnknown(m) +} + +var xxx_messageInfo_AllowList proto.InternalMessageInfo + +func (m *AllowList) GetAddresses() []string { + if m != nil { + return m.Addresses + } + return nil +} + func init() { proto.RegisterType((*Params)(nil), "cosmos.bank.v1beta1.Params") proto.RegisterType((*SendEnabled)(nil), "cosmos.bank.v1beta1.SendEnabled") @@ -425,49 +470,53 @@ func init() { proto.RegisterType((*Supply)(nil), "cosmos.bank.v1beta1.Supply") proto.RegisterType((*DenomUnit)(nil), "cosmos.bank.v1beta1.DenomUnit") proto.RegisterType((*Metadata)(nil), "cosmos.bank.v1beta1.Metadata") + proto.RegisterType((*AllowList)(nil), "cosmos.bank.v1beta1.AllowList") } func init() { proto.RegisterFile("cosmos/bank/v1beta1/bank.proto", fileDescriptor_dd052eee12edf988) } var fileDescriptor_dd052eee12edf988 = []byte{ - // 592 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbf, 0x6f, 0xd3, 0x40, - 0x14, 0xf6, 0x35, 0x8d, 0x49, 0x2f, 0xb0, 0x1c, 0x15, 0x72, 0x23, 0x61, 0x1b, 0x4b, 0x48, 0x29, - 0xa2, 0x4e, 0x0a, 0x0c, 0x28, 0x0b, 0x52, 0xca, 0x0f, 0x75, 0x40, 0x20, 0x57, 0x08, 0x09, 0x86, - 0xe8, 0x9c, 0xbb, 0x06, 0xab, 0xf6, 0x9d, 0x95, 0x3b, 0x57, 0xf5, 0x7f, 0xc0, 0x04, 0x8c, 0x8c, - 0x9d, 0x59, 0xe1, 0x7f, 0xa0, 0x63, 0x05, 0x0b, 0x53, 0x40, 0xc9, 0xc2, 0xdc, 0xbf, 0x00, 0xf9, - 0xce, 0xf9, 0x51, 0x29, 0x20, 0x06, 0x06, 0xa6, 0xbc, 0xef, 0xbd, 0xef, 0x7d, 0xef, 0xe9, 0xbb, - 0xe7, 0x40, 0xbb, 0xcf, 0x45, 0xc2, 0x45, 0x2b, 0xc4, 0xec, 0xa0, 0x75, 0xb8, 0x1d, 0x52, 0x89, - 0xb7, 0x15, 0xf0, 0xd3, 0x21, 0x97, 0x1c, 0x5d, 0xd6, 0x75, 0x5f, 0xa5, 0xca, 0x7a, 0x63, 0x7d, - 0xc0, 0x07, 0x5c, 0xd5, 0x5b, 0x45, 0xa4, 0xa9, 0x8d, 0x0d, 0x4d, 0xed, 0xe9, 0x42, 0xd9, 0xa7, - 0x4b, 0xf3, 0x29, 0x82, 0xce, 0xa6, 0xf4, 0x79, 0xc4, 0x74, 0xdd, 0xfb, 0x0a, 0xa0, 0xf9, 0x14, - 0x0f, 0x71, 0x22, 0xd0, 0x3e, 0xbc, 0x28, 0x28, 0x23, 0x3d, 0xca, 0x70, 0x18, 0x53, 0x62, 0x01, - 0xb7, 0xd2, 0xac, 0xdf, 0x72, 0xfd, 0x25, 0x7b, 0xf8, 0x7b, 0x94, 0x91, 0x07, 0x9a, 0xd7, 0xbd, - 0x76, 0x36, 0x72, 0xae, 0xe6, 0x38, 0x89, 0x3b, 0xde, 0x62, 0xff, 0x4d, 0x9e, 0x44, 0x92, 0x26, - 0xa9, 0xcc, 0xbd, 0xa0, 0x2e, 0xe6, 0x7c, 0xf4, 0x12, 0xae, 0x13, 0xba, 0x8f, 0xb3, 0x58, 0xf6, - 0xce, 0xcd, 0x5b, 0x71, 0x41, 0xb3, 0xd6, 0xdd, 0x3c, 0x1b, 0x39, 0xd7, 0xb5, 0xda, 0x32, 0xd6, - 0xa2, 0x2a, 0x2a, 0x09, 0x0b, 0xcb, 0x74, 0x56, 0xdf, 0x1f, 0x3b, 0x86, 0xf7, 0x08, 0xd6, 0x17, - 0x92, 0x68, 0x1d, 0x56, 0x09, 0x65, 0x3c, 0xb1, 0x80, 0x0b, 0x9a, 0x6b, 0x81, 0x06, 0xc8, 0x82, - 0x17, 0xce, 0x8d, 0x0e, 0xa6, 0xb0, 0x53, 0x2b, 0x44, 0x7e, 0x1e, 0x3b, 0xc0, 0x7b, 0x03, 0x60, - 0x75, 0x97, 0xa5, 0x99, 0x2c, 0xd8, 0x98, 0x90, 0x21, 0x15, 0xa2, 0x54, 0x99, 0x42, 0x84, 0x61, - 0xb5, 0x30, 0x54, 0x58, 0x2b, 0xca, 0xb0, 0x8d, 0xb9, 0x61, 0x82, 0xce, 0x0c, 0xdb, 0xe1, 0x11, - 0xeb, 0xb6, 0x4f, 0x46, 0x8e, 0xf1, 0xe1, 0xbb, 0xd3, 0x1c, 0x44, 0xf2, 0x55, 0x16, 0xfa, 0x7d, - 0x9e, 0x94, 0xaf, 0x55, 0xfe, 0x6c, 0x09, 0x72, 0xd0, 0x92, 0x79, 0x4a, 0x85, 0x6a, 0x10, 0x81, - 0x56, 0xee, 0xd4, 0x5e, 0xeb, 0x85, 0x0c, 0xef, 0x2d, 0x80, 0xe6, 0x93, 0x4c, 0xfe, 0x47, 0x1b, - 0x7d, 0x04, 0xd0, 0xdc, 0xcb, 0xd2, 0x34, 0xce, 0x8b, 0xb9, 0x92, 0x4b, 0x1c, 0x97, 0xa7, 0xf3, - 0x6f, 0xe7, 0x2a, 0xe5, 0xce, 0xc3, 0x72, 0x2e, 0xf8, 0xf2, 0x69, 0xeb, 0xee, 0x8d, 0x3f, 0x76, - 0x1f, 0xe9, 0x4f, 0x2b, 0xa6, 0x03, 0xdc, 0xcf, 0x5b, 0x87, 0xed, 0x3b, 0x6d, 0x5f, 0xef, 0xb9, - 0x6b, 0x01, 0xef, 0x39, 0x5c, 0xbb, 0x5f, 0x5c, 0xc1, 0x33, 0x16, 0xc9, 0xdf, 0xdc, 0x47, 0x03, - 0xd6, 0xe8, 0x51, 0xca, 0x19, 0x65, 0x52, 0x1d, 0xc8, 0xa5, 0x60, 0x86, 0x95, 0xf7, 0x71, 0x84, - 0x05, 0x15, 0x56, 0xc5, 0xad, 0x28, 0xef, 0x35, 0xf4, 0x3e, 0x03, 0x58, 0x7b, 0x4c, 0x25, 0x26, - 0x58, 0x62, 0xe4, 0xc2, 0x3a, 0xa1, 0xa2, 0x3f, 0x8c, 0x52, 0x19, 0x71, 0x56, 0xca, 0x2f, 0xa6, - 0xd0, 0xbd, 0x82, 0xc1, 0x78, 0xd2, 0xcb, 0x58, 0x24, 0xa7, 0x0f, 0x66, 0x2f, 0xfd, 0xe6, 0x66, - 0xfb, 0x06, 0x90, 0x4c, 0x43, 0x81, 0x10, 0x5c, 0x2d, 0xec, 0xb5, 0x2a, 0x4a, 0x5b, 0xc5, 0xc5, - 0x76, 0x24, 0x12, 0x69, 0x8c, 0x73, 0x6b, 0x55, 0x5f, 0x46, 0x09, 0x0b, 0x36, 0xc3, 0x09, 0xb5, - 0xaa, 0x9a, 0x5d, 0xc4, 0xe8, 0x0a, 0x34, 0x45, 0x9e, 0x84, 0x3c, 0xb6, 0x4c, 0x95, 0x2d, 0x51, - 0x77, 0xe7, 0x64, 0x6c, 0x83, 0xd3, 0xb1, 0x0d, 0x7e, 0x8c, 0x6d, 0xf0, 0x6e, 0x62, 0x1b, 0xa7, - 0x13, 0xdb, 0xf8, 0x36, 0xb1, 0x8d, 0x17, 0x9b, 0x7f, 0xe3, 0xbb, 0x7a, 0xbc, 0xd0, 0x54, 0x7f, - 0x33, 0xb7, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x03, 0xbf, 0xe9, 0xee, 0x04, 0x00, 0x00, + // 630 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x54, 0xbd, 0x6e, 0x13, 0x41, + 0x10, 0xf6, 0xc6, 0x3f, 0xd8, 0x6b, 0x90, 0xd0, 0x62, 0xa1, 0x4b, 0x24, 0xee, 0xcc, 0x49, 0x48, + 0x0e, 0x22, 0x76, 0x12, 0x28, 0x90, 0x1b, 0x84, 0x43, 0x40, 0x91, 0x40, 0xa0, 0x8b, 0x10, 0x12, + 0x14, 0xd6, 0xda, 0xbb, 0x31, 0xa7, 0xdc, 0xed, 0x9e, 0xbc, 0x7b, 0x21, 0xf7, 0x06, 0x54, 0x40, + 0x49, 0x99, 0x9a, 0x16, 0xde, 0x81, 0x94, 0x11, 0x34, 0x54, 0x06, 0x25, 0x0d, 0x75, 0x9e, 0x00, + 0xed, 0x8f, 0x7f, 0x22, 0x19, 0x44, 0x41, 0x41, 0x75, 0xf3, 0xcd, 0x7c, 0xf3, 0xcd, 0x68, 0x66, + 0xf6, 0xa0, 0xdb, 0xe7, 0x22, 0xe6, 0xa2, 0xd5, 0xc3, 0x6c, 0xb7, 0xb5, 0xb7, 0xd6, 0xa3, 0x12, + 0xaf, 0x69, 0xd0, 0x4c, 0x86, 0x5c, 0x72, 0x74, 0xc9, 0xc4, 0x9b, 0xda, 0x65, 0xe3, 0x4b, 0xb5, + 0x01, 0x1f, 0x70, 0x1d, 0x6f, 0x29, 0xcb, 0x50, 0x97, 0x16, 0x0d, 0xb5, 0x6b, 0x02, 0x36, 0xcf, + 0x84, 0xa6, 0x55, 0x04, 0x9d, 0x54, 0xe9, 0xf3, 0x90, 0x99, 0xb8, 0xff, 0x15, 0xc0, 0xd2, 0x13, + 0x3c, 0xc4, 0xb1, 0x40, 0x3b, 0xf0, 0xbc, 0xa0, 0x8c, 0x74, 0x29, 0xc3, 0xbd, 0x88, 0x12, 0x07, + 0xd4, 0xf3, 0x8d, 0xea, 0x7a, 0xbd, 0x39, 0xa7, 0x8f, 0xe6, 0x36, 0x65, 0x64, 0xd3, 0xf0, 0x3a, + 0x57, 0x4f, 0x47, 0xde, 0x95, 0x0c, 0xc7, 0x51, 0xdb, 0x9f, 0xcd, 0xbf, 0xc1, 0xe3, 0x50, 0xd2, + 0x38, 0x91, 0x99, 0x1f, 0x54, 0xc5, 0x94, 0x8f, 0x5e, 0xc0, 0x1a, 0xa1, 0x3b, 0x38, 0x8d, 0x64, + 0xf7, 0x4c, 0xbd, 0x85, 0x3a, 0x68, 0x94, 0x3b, 0xcb, 0xa7, 0x23, 0xef, 0x9a, 0x51, 0x9b, 0xc7, + 0x9a, 0x55, 0x45, 0x96, 0x30, 0xd3, 0x4c, 0xbb, 0xf0, 0xfe, 0xc0, 0xcb, 0xf9, 0x0f, 0x60, 0x75, + 0xc6, 0x89, 0x6a, 0xb0, 0x48, 0x28, 0xe3, 0xb1, 0x03, 0xea, 0xa0, 0x51, 0x09, 0x0c, 0x40, 0x0e, + 0x3c, 0x77, 0xa6, 0x74, 0x30, 0x86, 0xed, 0xb2, 0x12, 0xf9, 0x79, 0xe0, 0x01, 0xff, 0x0d, 0x80, + 0xc5, 0x2d, 0x96, 0xa4, 0x52, 0xb1, 0x31, 0x21, 0x43, 0x2a, 0x84, 0x55, 0x19, 0x43, 0x84, 0x61, + 0x51, 0x0d, 0x54, 0x38, 0x0b, 0x7a, 0x60, 0x8b, 0xd3, 0x81, 0x09, 0x3a, 0x19, 0xd8, 0x06, 0x0f, + 0x59, 0x67, 0xf5, 0x70, 0xe4, 0xe5, 0x3e, 0x7c, 0xf7, 0x1a, 0x83, 0x50, 0xbe, 0x4c, 0x7b, 0xcd, + 0x3e, 0x8f, 0xed, 0xb6, 0xec, 0x67, 0x45, 0x90, 0xdd, 0x96, 0xcc, 0x12, 0x2a, 0x74, 0x82, 0x08, + 0x8c, 0x72, 0xbb, 0xfc, 0xda, 0x34, 0x94, 0xf3, 0xdf, 0x02, 0x58, 0x7a, 0x9c, 0xca, 0xff, 0xa8, + 0xa3, 0x8f, 0x00, 0x96, 0xb6, 0xd3, 0x24, 0x89, 0x32, 0x55, 0x57, 0x72, 0x89, 0x23, 0x7b, 0x3a, + 0xff, 0xb6, 0xae, 0x56, 0x6e, 0xdf, 0xb7, 0x75, 0xc1, 0x97, 0x4f, 0x2b, 0xb7, 0xaf, 0xff, 0x31, + 0x7b, 0xdf, 0x3c, 0xad, 0x88, 0x0e, 0x70, 0x3f, 0x6b, 0xed, 0xad, 0xde, 0x5a, 0x6d, 0x9a, 0x3e, + 0xb7, 0x1c, 0xe0, 0x3f, 0x83, 0x95, 0x7b, 0xea, 0x0a, 0x9e, 0xb2, 0x50, 0xfe, 0xe6, 0x3e, 0x96, + 0x60, 0x99, 0xee, 0x27, 0x9c, 0x51, 0x26, 0xf5, 0x81, 0x5c, 0x08, 0x26, 0x58, 0xcf, 0x3e, 0x0a, + 0xb1, 0xa0, 0xc2, 0xc9, 0xd7, 0xf3, 0x7a, 0xf6, 0x06, 0xfa, 0x9f, 0x01, 0x2c, 0x3f, 0xa2, 0x12, + 0x13, 0x2c, 0x31, 0xaa, 0xc3, 0x2a, 0xa1, 0xa2, 0x3f, 0x0c, 0x13, 0x19, 0x72, 0x66, 0xe5, 0x67, + 0x5d, 0xe8, 0x8e, 0x62, 0x30, 0x1e, 0x77, 0x53, 0x16, 0xca, 0xf1, 0xc2, 0xdc, 0xb9, 0x6f, 0x6e, + 0xd2, 0x6f, 0x00, 0xc9, 0xd8, 0x14, 0x08, 0xc1, 0x82, 0x1a, 0xaf, 0x93, 0xd7, 0xda, 0xda, 0x56, + 0xdd, 0x91, 0x50, 0x24, 0x11, 0xce, 0x9c, 0x82, 0xb9, 0x0c, 0x0b, 0x15, 0x9b, 0xe1, 0x98, 0x3a, + 0x45, 0xc3, 0x56, 0x36, 0xba, 0x0c, 0x4b, 0x22, 0x8b, 0x7b, 0x3c, 0x72, 0x4a, 0xda, 0x6b, 0x91, + 0xbf, 0x09, 0x2b, 0x77, 0xa3, 0x88, 0xbf, 0x7a, 0x18, 0x0a, 0x89, 0xd6, 0x61, 0xc5, 0x5e, 0x17, + 0x15, 0x7a, 0xbd, 0x95, 0x4e, 0xed, 0x74, 0xe4, 0x5d, 0x34, 0x2f, 0x75, 0x12, 0xf2, 0x83, 0x29, + 0xad, 0x5d, 0x50, 0x7b, 0xea, 0x6c, 0x1c, 0x1e, 0xbb, 0xe0, 0xe8, 0xd8, 0x05, 0x3f, 0x8e, 0x5d, + 0xf0, 0xee, 0xc4, 0xcd, 0x1d, 0x9d, 0xb8, 0xb9, 0x6f, 0x27, 0x6e, 0xee, 0xf9, 0xf2, 0xdf, 0xac, + 0x4f, 0xdf, 0x40, 0xaf, 0xa4, 0xff, 0x56, 0x37, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xb3, 0xd4, + 0xc4, 0xae, 0x35, 0x05, 0x00, 0x00, } func (this *SendEnabled) Equal(that interface{}) bool { @@ -526,6 +575,35 @@ func (this *Supply) Equal(that interface{}) bool { } return true } +func (this *AllowList) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*AllowList) + if !ok { + that2, ok := that.(AllowList) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if len(this.Addresses) != len(that1.Addresses) { + return false + } + for i := range this.Addresses { + if this.Addresses[i] != that1.Addresses[i] { + return false + } + } + return true +} func (m *Params) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -854,6 +932,38 @@ func (m *Metadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *AllowList) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AllowList) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AllowList) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintBank(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintBank(dAtA []byte, offset int, v uint64) int { offset -= sovBank(v) base := offset @@ -1009,6 +1119,21 @@ func (m *Metadata) Size() (n int) { return n } +func (m *AllowList) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovBank(uint64(l)) + } + } + return n +} + func sovBank(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1914,6 +2039,88 @@ func (m *Metadata) Unmarshal(dAtA []byte) error { } return nil } +func (m *AllowList) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AllowList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AllowList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBank + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthBank + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthBank + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipBank(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthBank + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipBank(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/bank/types/key.go b/x/bank/types/key.go index 97b8f1268..4ead5e166 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -28,10 +28,11 @@ var ( WeiBalancesPrefix = []byte{0x04} // BalancesPrefix is the prefix for the account balances store. We use a byte // (instead of `[]byte("balances")` to save some disk space). - DeferredCachePrefix = []byte{0x03} - BalancesPrefix = []byte{0x02} - SupplyKey = []byte{0x00} - DenomMetadataPrefix = []byte{0x1} + DeferredCachePrefix = []byte{0x03} + BalancesPrefix = []byte{0x02} + SupplyKey = []byte{0x00} + DenomMetadataPrefix = []byte{0x1} + DenomAllowListPrefix = []byte{0x11} ) // DenomMetadataKey returns the denomination metadata key. @@ -40,6 +41,12 @@ func DenomMetadataKey(denom string) []byte { return append(DenomMetadataPrefix, d...) } +// DenomAllowListKey returns the denomination allow list key. +func DenomAllowListKey(denom string) []byte { + d := []byte(denom) + return append(DenomAllowListPrefix, d...) +} + // AddressFromBalancesStore returns an account address from a balances prefix // store. The key must not contain the prefix BalancesPrefix as the prefix store // iterator discards the actual prefix.