diff --git a/CHANGELOG.md b/CHANGELOG.md index 89b3583e7f..5ef8631696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (client) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) `client/tx.PrepareFactory` has been converted to a private function, as it's only used internally. * (auth/tx) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `ProtoTxProvider` interface used as a workaround for transaction simulation has been removed. * (x/bank) [\#8798](https://github.com/cosmos/cosmos-sdk/pull/8798) `GetTotalSupply` is removed in favour of `GetPaginatedTotalSupply` +* (x/bank/types) [\#9061](https://github.com/cosmos/cosmos-sdk/pull/9061) `AddressFromBalancesStore` now returns an error for invalid key instead of panic. ### State Machine Breaking @@ -101,6 +102,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (types) [\#8962](https://github.com/cosmos/cosmos-sdk/issues/8962) Add `Abs()` method to `sdk.Int`. * (x/bank) [\#8950](https://github.com/cosmos/cosmos-sdk/pull/8950) Improve efficiency on supply updates. * (store) [\#8012](https://github.com/cosmos/cosmos-sdk/pull/8012) Implementation of ADR-038 WriteListener and listen.KVStore +* (makefile) [\#7933](https://github.com/cosmos/cosmos-sdk/issues/7933) Use Docker to generate swagger files. ### Bug Fixes diff --git a/Makefile b/Makefile index d7b8a1fa8c..a26681ea8e 100644 --- a/Makefile +++ b/Makefile @@ -386,13 +386,13 @@ proto-gen-any: $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protocgen-any.sh proto-swagger-gen: - @./scripts/protoc-swagger-gen.sh + $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace tendermintdev/sdk-proto-gen sh ./scripts/protoc-swagger-gen.sh proto-lint: - @$(DOCKER_BUF) check lint --error-format=json + @$(DOCKER_BUF) lint --error-format=json proto-check-breaking: - @$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master + @$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=master TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint GOGO_PROTO_URL = https://raw.githubusercontent.com/regen-network/protobuf/cosmos diff --git a/client/docs/swagger-ui/swagger.yaml b/client/docs/swagger-ui/swagger.yaml index 0a96bc1d79..1fbc6279ee 100644 --- a/client/docs/swagger-ui/swagger.yaml +++ b/client/docs/swagger-ui/swagger.yaml @@ -6265,6 +6265,24 @@ paths: signatures required by gogoproto. title: supply is the supply of the coins + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise title: >- QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC @@ -6292,6 +6310,62 @@ paths: value: type: string format: byte + parameters: + - name: pagination.key + description: |- + key is a value returned in PageResponse.next_key to begin + querying the next page most efficiently. Only one of offset or key + should be set. + in: query + required: false + type: string + format: byte + - name: pagination.offset + description: >- + offset is a numeric offset that can be used when key is unavailable. + + It is less efficient than using key. Only one of offset or key + should + + be set. + in: query + required: false + type: string + format: uint64 + - name: pagination.limit + description: >- + limit is the total number of results to be returned in the result + page. + + If left empty it will default to a value to be set by each app. + in: query + required: false + type: string + format: uint64 + - name: pagination.count_total + description: >- + count_total is set to true to indicate that the result set should + include + + a count of the total number of items available for pagination in + UIs. + + count_total is only respected when offset is used. It is ignored + when key + + is set. + in: query + required: false + type: boolean + format: boolean + - name: pagination.reverse + description: >- + reverse is set to true indicates that, results to be returned in the + descending order. + in: query + required: false + type: boolean + format: boolean tags: - Query '/cosmos/bank/v1beta1/supply/{denom}': @@ -24029,6 +24103,24 @@ definitions: NOTE: The amount field is an Int which implements the custom method signatures required by gogoproto. title: supply is the supply of the coins + pagination: + description: pagination defines the pagination in the response. + type: object + properties: + next_key: + type: string + format: byte + title: |- + next_key is the key to be passed to PageRequest.key to + query the next page most efficiently + total: + type: string + format: uint64 + title: >- + total is total number of results available if + PageRequest.count_total + + was set, its value is undefined otherwise title: >- QueryTotalSupplyResponse is the response type for the Query/TotalSupply RPC diff --git a/contrib/devtools/dockerfile b/contrib/devtools/dockerfile index 565e66ea33..10be413453 100644 --- a/contrib/devtools/dockerfile +++ b/contrib/devtools/dockerfile @@ -18,4 +18,10 @@ RUN GO111MODULE=on go get \ RUN GO111MODULE=on go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc +RUN apk add --no-cache \ + nodejs \ + npm + +RUN npm install -g swagger-combine + COPY --from=BUILDER /usr/local/bin /usr/local/bin diff --git a/x/auth/client/cli/cli_test.go b/x/auth/client/cli/cli_test.go index 3f5cec3ab4..15f95d92b1 100644 --- a/x/auth/client/cli/cli_test.go +++ b/x/auth/client/cli/cli_test.go @@ -78,11 +78,17 @@ func (s *IntegrationTestSuite) TearDownSuite() { func (s *IntegrationTestSuite) TestCLIValidateSignatures() { val := s.network.Validators[0] - res := s.createBankMsg(val, val.Address) + sendTokens := sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))) + + res, err := s.createBankMsg(val, val.Address, sendTokens, + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) + s.Require().NoError(err) // write unsigned tx to file unsignedTx := testutil.WriteToNewTempFile(s.T(), res.String()) - res, err := authtest.TxSignExec(val.ClientCtx, val.Address, unsignedTx.Name()) + res, err = authtest.TxSignExec(val.ClientCtx, val.Address, unsignedTx.Name()) s.Require().NoError(err) signedTx, err := val.ClientCtx.TxConfig.TxJSONDecoder()(res.Bytes()) s.Require().NoError(err) @@ -104,7 +110,15 @@ func (s *IntegrationTestSuite) TestCLIValidateSignatures() { func (s *IntegrationTestSuite) TestCLISignBatch() { val := s.network.Validators[0] - generatedStd := s.createBankMsg(val, val.Address) + var sendTokens = sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + ) + + generatedStd, err := s.createBankMsg(val, val.Address, + sendTokens, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) + s.Require().NoError(err) + outputFile := testutil.WriteToNewTempFile(s.T(), strings.Repeat(generatedStd.String(), 3)) val.ClientCtx.HomeDir = strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1) @@ -136,13 +150,19 @@ func (s *IntegrationTestSuite) TestCLISign_AminoJSON() { require := s.Require() val1 := s.network.Validators[0] txCfg := val1.ClientCtx.TxConfig - txBz := s.createBankMsg(val1, val1.Address) + var sendTokens = sdk.NewCoins( + sdk.NewCoin(fmt.Sprintf("%stoken", val1.Moniker), sdk.NewInt(10)), + sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), + ) + txBz, err := s.createBankMsg(val1, val1.Address, + sendTokens, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) + require.NoError(err) fileUnsigned := testutil.WriteToNewTempFile(s.T(), txBz.String()) chainFlag := fmt.Sprintf("--%s=%s", flags.FlagChainID, val1.ClientCtx.ChainID) sigOnlyFlag := "--signature-only" signModeAminoFlag := "--sign-mode=amino-json" - // SIC! validators have same key names and same adddresses as those registered in the keyring, + // SIC! validators have same key names and same addresses as those registered in the keyring, // BUT the keys are different! valInfo, err := val1.ClientCtx.Keyring.Key(val1.Moniker) require.NoError(err) @@ -245,15 +265,9 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmd() { s.Require().NoError(s.network.WaitForNextBlock()) // Service Msg. - out, err := bankcli.MsgSendExec( - val.ClientCtx, - val.Address, - account2.GetAddress(), + out, err := s.createBankMsg( + val, account2.GetAddress(), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) var txRes sdk.TxResponse @@ -321,16 +335,8 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) - normalGeneratedTx, err := bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, - account.GetAddress(), - sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - ) + normalGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), + sdk.NewCoins(sendTokens), fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) s.Require().NoError(err) txCfg := val1.ClientCtx.TxConfig @@ -346,15 +352,8 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { s.Require().Equal(0, len(sigs)) // Test generate sendTx with --gas=$amount - limitedGasGeneratedTx, err := bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, - account.GetAddress(), - sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", 100), + limitedGasGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), + sdk.NewCoins(sendTokens), fmt.Sprintf("--gas=%d", 100), fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), ) s.Require().NoError(err) @@ -378,17 +377,9 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { startTokens := balRes.Balances.AmountOf(s.cfg.BondDenom) // Test generate sendTx, estimate gas - finalGeneratedTx, err := bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, - account.GetAddress(), - sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - ) + finalGeneratedTx, err := s.createBankMsg(val1, account.GetAddress(), + sdk.NewCoins(sendTokens), fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly)) s.Require().NoError(err) finalStdTx, err := txCfg.TxJSONDecoder()(finalGeneratedTx.Bytes()) @@ -478,9 +469,9 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { } func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() { - val1 := *s.network.Validators[0] + val1 := s.network.Validators[0] - // Generate 2 accounts and a multisig. + // Fetch account and a multisig info account1, err := val1.ClientCtx.Keyring.Key("newAccount1") s.Require().NoError(err) @@ -488,17 +479,12 @@ func (s *IntegrationTestSuite) TestCLIMultisignInsufficientCosigners() { s.Require().NoError(err) // Send coins from validator to multisig. - _, err = bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, + _, err = s.createBankMsg( + val1, multisigInfo.GetAddress(), sdk.NewCoins( sdk.NewInt64Coin(s.cfg.BondDenom, 10), ), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) @@ -544,15 +530,11 @@ func (s *IntegrationTestSuite) TestCLIEncode() { sendTokens := sdk.NewCoin(s.cfg.BondDenom, sdk.TokensFromConsensusPower(10, sdk.DefaultPowerReduction)) - normalGeneratedTx, err := bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, - val1.Address, + normalGeneratedTx, err := s.createBankMsg( + val1, val1.Address, sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), "--memo", "deadbeef", + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), + fmt.Sprintf("--%s=deadbeef", flags.FlagMemo), ) s.Require().NoError(err) savedTxFile := testutil.WriteToNewTempFile(s.T(), normalGeneratedTx.String()) @@ -575,7 +557,7 @@ func (s *IntegrationTestSuite) TestCLIEncode() { } func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() { - val1 := *s.network.Validators[0] + val1 := s.network.Validators[0] // Generate 2 accounts and a multisig. account1, err := val1.ClientCtx.Keyring.Key("newAccount1") @@ -597,15 +579,10 @@ func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() { // Send coins from validator to multisig. sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - _, err = bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, + _, err = s.createBankMsg( + val1, multisigInfo.GetAddress(), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) @@ -667,7 +644,7 @@ func (s *IntegrationTestSuite) TestCLIMultisignSortSignatures() { } func (s *IntegrationTestSuite) TestCLIMultisign() { - val1 := *s.network.Validators[0] + val1 := s.network.Validators[0] // Generate 2 accounts and a multisig. account1, err := val1.ClientCtx.Keyring.Key("newAccount1") @@ -681,15 +658,9 @@ func (s *IntegrationTestSuite) TestCLIMultisign() { // Send coins from validator to multisig. sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - _, err = bankcli.MsgSendExec( - val1.ClientCtx, - val1.Address, - multisigInfo.GetAddress(), + _, err = s.createBankMsg( + val1, multisigInfo.GetAddress(), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(s.network.WaitForNextBlock()) @@ -766,15 +737,10 @@ func (s *IntegrationTestSuite) TestSignBatchMultisig() { // Send coins from validator to multisig. sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) - _, err = bankcli.MsgSendExec( - val.ClientCtx, - val.Address, + _, err = s.createBankMsg( + val, multisigInfo.GetAddress(), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) s.Require().NoError(s.network.WaitForNextBlock()) @@ -827,15 +793,10 @@ func (s *IntegrationTestSuite) TestMultisignBatch() { // Send coins from validator to multisig. sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 1000) - _, err = bankcli.MsgSendExec( - val.ClientCtx, - val.Address, + _, err = s.createBankMsg( + val, multisigInfo.GetAddress(), sdk.NewCoins(sendTokens), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), - fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--gas=%d", flags.DefaultGasLimit), ) s.Require().NoError(err) s.Require().NoError(s.network.WaitForNextBlock()) @@ -886,7 +847,6 @@ func (s *IntegrationTestSuite) TestMultisignBatch() { signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx) val.ClientCtx.BroadcastMode = flags.BroadcastBlock res, err = authtest.TxBroadcastExec(val.ClientCtx, signedTxFile.Name()) - s.T().Log(res) s.Require().NoError(err) s.Require().NoError(s.network.WaitForNextBlock()) } @@ -1099,7 +1059,7 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() { // Creating a tx with 2 msgs from 2 signers: val0 and val1. // The validators need to sign with SIGN_MODE_LEGACY_AMINO_JSON, // because DIRECT doesn't support multi signers via the CLI. - // Since we we amino, we don't need to pre-populate signer_infos. + // Since we use amino, we don't need to pre-populate signer_infos. txBuilder := val0.ClientCtx.TxConfig.NewTxBuilder() txBuilder.SetMsgs( banktypes.NewMsgSend(val0.Address, addr1, sdk.NewCoins(val0Coin)), @@ -1150,22 +1110,15 @@ func (s *IntegrationTestSuite) TestSignWithMultiSigners_AminoJSON() { require.Equal(sdk.NewCoins(val0Coin, val1Coin), queryRes.Balances) } -func (s *IntegrationTestSuite) createBankMsg(val *network.Validator, toAddr sdk.AccAddress) testutil.BufferWriter { - res, err := bankcli.MsgSendExec( - val.ClientCtx, - val.Address, - toAddr, - sdk.NewCoins( - sdk.NewCoin(fmt.Sprintf("%stoken", val.Moniker), sdk.NewInt(10)), - sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)), - ), - fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), +func (s *IntegrationTestSuite) createBankMsg(val *network.Validator, toAddr sdk.AccAddress, amount sdk.Coins, extraFlags ...string) (testutil.BufferWriter, error) { + flags := []string{fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), - fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), - fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), - ) - s.Require().NoError(err) - return res + fmt.Sprintf("--%s=%s", flags.FlagFees, + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + } + + flags = append(flags, extraFlags...) + return bankcli.MsgSendExec(val.ClientCtx, val.Address, toAddr, amount, flags...) } func TestIntegrationTestSuite(t *testing.T) { diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 7271bba059..d2a560cb78 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -140,7 +140,13 @@ func (k BaseViewKeeper) IterateAllBalances(ctx sdk.Context, cb func(sdk.AccAddre defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - address := types.AddressFromBalancesStore(iterator.Key()) + address, err := types.AddressFromBalancesStore(iterator.Key()) + if err != nil { + k.Logger(ctx).With("key", iterator.Key(), "err", err).Error("failed to get address from balances store") + // TODO: revisit, for now, panic here to keep same behavior as in 0.42 + // ref: https://github.com/cosmos/cosmos-sdk/issues/7409 + panic(err) + } var balance sdk.Coin k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &balance) diff --git a/x/bank/types/errors.go b/x/bank/types/errors.go index ab88b40e9b..8446d957b6 100644 --- a/x/bank/types/errors.go +++ b/x/bank/types/errors.go @@ -11,4 +11,5 @@ var ( ErrInputOutputMismatch = sdkerrors.Register(ModuleName, 4, "sum inputs != sum outputs") ErrSendDisabled = sdkerrors.Register(ModuleName, 5, "send transactions are disabled") ErrDenomMetadataNotFound = sdkerrors.Register(ModuleName, 6, "client denom metadata not found") + ErrInvalidKey = sdkerrors.Register(ModuleName, 7, "invalid key") ) diff --git a/x/bank/types/key.go b/x/bank/types/key.go index 858fd2480a..0d8ec96a0d 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -37,11 +37,19 @@ func DenomMetadataKey(denom string) []byte { // AddressFromBalancesStore returns an account address from a balances prefix // store. The key must not contain the perfix BalancesPrefix as the prefix store // iterator discards the actual prefix. -func AddressFromBalancesStore(key []byte) sdk.AccAddress { +// +// If invalid key is passed, AddressFromBalancesStore returns ErrInvalidKey. +func AddressFromBalancesStore(key []byte) (sdk.AccAddress, error) { + if len(key) == 0 { + return nil, ErrInvalidKey + } addrLen := key[0] + if len(key[1:]) < int(addrLen) { + return nil, ErrInvalidKey + } addr := key[1 : addrLen+1] - return sdk.AccAddress(addr) + return sdk.AccAddress(addr), nil } // CreateAccountBalancesPrefix creates the prefix for an account's balances. diff --git a/x/bank/types/key_test.go b/x/bank/types/key_test.go index 206ef8335c..c54037a227 100644 --- a/x/bank/types/key_test.go +++ b/x/bank/types/key_test.go @@ -1,8 +1,10 @@ package types_test import ( + "errors" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" @@ -24,6 +26,27 @@ func TestAddressFromBalancesStore(t *testing.T) { require.Equal(t, 20, addrLen) key := cloneAppend(address.MustLengthPrefix(addr), []byte("stake")) - res := types.AddressFromBalancesStore(key) + res, err := types.AddressFromBalancesStore(key) + require.NoError(t, err) require.Equal(t, res, addr) } + +func TestInvalidAddressFromBalancesStore(t *testing.T) { + tests := []struct { + name string + key []byte + }{ + {"empty", []byte("")}, + {"invalid", []byte("3AA")}, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + _, err := types.AddressFromBalancesStore(tc.key) + assert.Error(t, err) + assert.True(t, errors.Is(types.ErrInvalidKey, err)) + }) + } +}