From ce1e65083ea7fcbb744a3927de8be80f1f1bf77f Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 2 Aug 2021 10:16:23 +0200 Subject: [PATCH 01/31] adding checks to ensure denoms are sorted --- x/bank/types/balance.go | 13 ++++++++++--- x/bank/types/balance_test.go | 12 ++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/x/bank/types/balance.go b/x/bank/types/balance.go index b944fea27f85..2aa7967d5060 100644 --- a/x/bank/types/balance.go +++ b/x/bank/types/balance.go @@ -34,6 +34,11 @@ func (b Balance) Validate() error { if err != nil { return err } + + var prevDenom string + if !b.Coins.Empty() { + prevDenom = b.Coins[0].Denom + } seenDenoms := make(map[string]bool) // NOTE: we perform a custom validation since the coins.Validate function @@ -47,16 +52,18 @@ func (b Balance) Validate() error { return err } + if coin.Denom < prevDenom { + return fmt.Errorf("denomination %s is not sorted", coin.Denom) + } + if coin.IsNegative() { return fmt.Errorf("coin %s amount is cannot be negative", coin.Denom) } seenDenoms[coin.Denom] = true + prevDenom = coin.Denom } - // sort the coins post validation - b.Coins = b.Coins.Sort() - return nil } diff --git a/x/bank/types/balance_test.go b/x/bank/types/balance_test.go index f328314910b7..1c7d4255c76b 100644 --- a/x/bank/types/balance_test.go +++ b/x/bank/types/balance_test.go @@ -64,6 +64,18 @@ func TestBalanceValidate(t *testing.T) { }, true, }, + { + "unsorted coins", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.NewInt64Coin("atom", 2), + sdk.NewInt64Coin("zatom", 2), + sdk.NewInt64Coin("batom", 12), + }, + }, + true, + }, } for _, tc := range testCases { From 2cea5e5750d220d476ddc3184d19cdf0a7de52c5 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Mon, 2 Aug 2021 10:30:16 +0200 Subject: [PATCH 02/31] updating changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4b82b50c37..8861b7145cab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. * [\#9762](https://github.com/cosmos/cosmos-sdk/pull/9762) The init command uses the chain-id from the client config if --chain-id is not provided * [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. +* [\#9829](https://github.com/cosmos/cosmos-sdk/pull/9829) Fixed Coin denom sorting not being checked during `Balance.Validate` check ### State Machine Breaking From 57ba1e23c82b7723a0236d961e17e8b7fdc7d663 Mon Sep 17 00:00:00 2001 From: billy rennekamp Date: Mon, 2 Aug 2021 19:03:40 +0200 Subject: [PATCH 03/31] docs: Fix broken link in upgrade.md (#9830) this PR (https://github.com/cosmos/cosmos-sdk/pull/9822/files) didn't actually fix the problem. The number of `..` you need depends on whether `core` or `master` is included in the URL. ## Description Closes: #XXXX --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- docs/core/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index 5b8366e9441f..90919870b2e4 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -12,7 +12,7 @@ Upgrade your app modules smoothly with custom in-place store migration logic. {s The Cosmos SDK uses two methods to perform upgrades. -- Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See [Chain Upgrade Guide to v0.42](../v0.42/migrations/chain-upgrade-guide-040.html). +- Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See [Chain Upgrade Guide to v0.42](https://docs.cosmos.network/v0.42/migrations/chain-upgrade-guide-040.html). - Version v0.43 and later can perform upgrades in place to significantly decrease the upgrade time for chains with a larger state. Use the [Module Upgrade Guide](../building-modules/upgrade.md) to set up your application modules to take advantage of in-place upgrades. From 42e28e294be4ce9a7f33d2d7262efd689b51c11b Mon Sep 17 00:00:00 2001 From: "Changhyun, Park" <45252226+mattverse@users.noreply.github.com> Date: Tue, 3 Aug 2021 03:47:43 +0900 Subject: [PATCH 04/31] feat: Add cli for listing all module accounts (#9812) ## Description This PR adds a CLI for querying all module accounts, `module-accounts`, in the auth module. Using this command would display all the account information, including human readable name of the module, module account address, account number, permissions, etc. This command would be especially useful for developers using cosmos-sdk as there are lots of instances where a developer would have to look into the module account, but only to do so by getting the module account address after getting the module name, hashing it and bech32fying it in the status quo. By using this command, users would be able to get a quick look on the list of all module accounts' information. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [x] reviewed tests and test coverage - [x] manually tested (if applicable) --- docs/core/proto-docs.md | 28 ++ proto/cosmos/auth/v1beta1/query.proto | 13 + x/auth/client/cli/query.go | 29 ++ x/auth/keeper/grpc_query.go | 25 ++ x/auth/keeper/grpc_query_test.go | 81 +++++ x/auth/types/query.pb.go | 414 +++++++++++++++++++++++--- x/auth/types/query.pb.gw.go | 62 ++++ 7 files changed, 617 insertions(+), 35 deletions(-) diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 0502d41e69a9..6b68c21ae232 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -21,6 +21,8 @@ - [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) - [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest) - [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse) + - [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest) + - [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse) - [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) - [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) @@ -898,6 +900,31 @@ QueryAccountsResponse is the response type for the Query/Accounts RPC method. + + +### QueryModuleAccountsRequest +QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method. + + + + + + + + +### QueryModuleAccountsResponse +QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `accounts` | [google.protobuf.Any](#google.protobuf.Any) | repeated | | + + + + + + ### QueryParamsRequest @@ -939,6 +966,7 @@ Query defines the gRPC querier service. | `Accounts` | [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest) | [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse) | Accounts returns all the existing accounts | GET|/cosmos/auth/v1beta1/accounts| | `Account` | [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) | [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) | Account returns account details based on address. | GET|/cosmos/auth/v1beta1/accounts/{address}| | `Params` | [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) | Params queries all parameters. | GET|/cosmos/auth/v1beta1/params| +| `ModuleAccounts` | [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest) | [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse) | ModuleAccounts returns all the existing Module Accounts. | GET|/cosmos/auth/v1beta1/module_accounts| diff --git a/proto/cosmos/auth/v1beta1/query.proto b/proto/cosmos/auth/v1beta1/query.proto index 76d30dd610cc..14bec85a3af5 100644 --- a/proto/cosmos/auth/v1beta1/query.proto +++ b/proto/cosmos/auth/v1beta1/query.proto @@ -26,6 +26,11 @@ service Query { rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/cosmos/auth/v1beta1/params"; } + + // ModuleAccounts returns all the existing module accounts. + rpc ModuleAccounts(QueryModuleAccountsRequest) returns (QueryModuleAccountsResponse) { + option (google.api.http).get = "/cosmos/auth/v1beta1/module_accounts"; + } } // QueryAccountsRequest is the request type for the Query/Accounts RPC method. @@ -66,3 +71,11 @@ message QueryParamsResponse { // params defines the parameters of the module. Params params = 1 [(gogoproto.nullable) = false]; } + +// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method. +message QueryModuleAccountsRequest {} + +// QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method. +message QueryModuleAccountsResponse { + repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"]; +} \ No newline at end of file diff --git a/x/auth/client/cli/query.go b/x/auth/client/cli/query.go index ce4f6e95921d..f76f64d1058a 100644 --- a/x/auth/client/cli/query.go +++ b/x/auth/client/cli/query.go @@ -1,6 +1,7 @@ package cli import ( + "context" "fmt" "strings" @@ -42,6 +43,7 @@ func GetQueryCmd() *cobra.Command { GetAccountCmd(), GetAccountsCmd(), QueryParamsCmd(), + QueryModuleAccountsCmd(), ) return cmd @@ -142,6 +144,33 @@ func GetAccountsCmd() *cobra.Command { return cmd } +// QueryAllModuleAccountsCmd returns a list of all the existing module accounts with their account information and permissions +func QueryModuleAccountsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "module-accounts", + Short: "Query all module accounts", + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + + queryClient := types.NewQueryClient(clientCtx) + + res, err := queryClient.ModuleAccounts(context.Background(), &types.QueryModuleAccountsRequest{}) + if err != nil { + return err + } + + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} + // QueryTxsByEventsCmd returns a command to search through transactions by events. func QueryTxsByEventsCmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/x/auth/keeper/grpc_query.go b/x/auth/keeper/grpc_query.go index 4305c1cd0a8e..1e0fe1c928a2 100644 --- a/x/auth/keeper/grpc_query.go +++ b/x/auth/keeper/grpc_query.go @@ -83,3 +83,28 @@ func (ak AccountKeeper) Params(c context.Context, req *types.QueryParamsRequest) return &types.QueryParamsResponse{Params: params}, nil } + +// ModuleAccounts returns all the existing Module Accounts +func (ak AccountKeeper) ModuleAccounts(c context.Context, req *types.QueryModuleAccountsRequest) (*types.QueryModuleAccountsResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "empty request") + } + + ctx := sdk.UnwrapSDKContext(c) + + modAccounts := make([]*codectypes.Any, 0, len(ak.permAddrs)) + + for moduleName := range ak.permAddrs { + account := ak.GetModuleAccount(ctx, moduleName) + if account == nil { + return nil, status.Errorf(codes.NotFound, "account %s not found", moduleName) + } + any, err := codectypes.NewAnyWithValue(account) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + modAccounts = append(modAccounts, any) + } + + return &types.QueryModuleAccountsResponse{Accounts: modAccounts}, nil +} diff --git a/x/auth/keeper/grpc_query_test.go b/x/auth/keeper/grpc_query_test.go index e593829a7172..052bdcbab20c 100644 --- a/x/auth/keeper/grpc_query_test.go +++ b/x/auth/keeper/grpc_query_test.go @@ -191,3 +191,84 @@ func (suite *KeeperTestSuite) TestGRPCQueryParameters() { }) } } + +func (suite *KeeperTestSuite) TestGRPCQueryModuleAccounts() { + var ( + req *types.QueryModuleAccountsRequest + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + posttests func(res *types.QueryModuleAccountsResponse) + }{ + { + "success", + func() { + req = &types.QueryModuleAccountsRequest{} + }, + true, + func(res *types.QueryModuleAccountsResponse) { + var mintModuleExists = false + for _, acc := range res.Accounts { + var account types.AccountI + err := suite.app.InterfaceRegistry().UnpackAny(acc, &account) + suite.Require().NoError(err) + + moduleAccount, ok := account.(types.ModuleAccountI) + + suite.Require().True(ok) + if moduleAccount.GetName() == "mint" { + mintModuleExists = true + } + } + suite.Require().True(mintModuleExists) + }, + }, + { + "invalid module name", + func() { + req = &types.QueryModuleAccountsRequest{} + }, + true, + func(res *types.QueryModuleAccountsResponse) { + var mintModuleExists = false + for _, acc := range res.Accounts { + var account types.AccountI + err := suite.app.InterfaceRegistry().UnpackAny(acc, &account) + suite.Require().NoError(err) + + moduleAccount, ok := account.(types.ModuleAccountI) + + suite.Require().True(ok) + if moduleAccount.GetName() == "falseCase" { + mintModuleExists = true + } + } + suite.Require().False(mintModuleExists) + }, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() // reset + + tc.malleate() + ctx := sdk.WrapSDKContext(suite.ctx) + + res, err := suite.queryClient.ModuleAccounts(ctx, req) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(res) + } else { + suite.Require().Error(err) + suite.Require().Nil(res) + } + + tc.posttests(res) + }) + } +} diff --git a/x/auth/types/query.pb.go b/x/auth/types/query.pb.go index 25c0a13630ad..61c7041a6a9d 100644 --- a/x/auth/types/query.pb.go +++ b/x/auth/types/query.pb.go @@ -301,6 +301,88 @@ func (m *QueryParamsResponse) GetParams() Params { return Params{} } +// QueryModuleAccountsRequest is the request type for the Query/ModuleAccounts RPC method. +type QueryModuleAccountsRequest struct { +} + +func (m *QueryModuleAccountsRequest) Reset() { *m = QueryModuleAccountsRequest{} } +func (m *QueryModuleAccountsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryModuleAccountsRequest) ProtoMessage() {} +func (*QueryModuleAccountsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{6} +} +func (m *QueryModuleAccountsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryModuleAccountsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryModuleAccountsRequest.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 *QueryModuleAccountsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryModuleAccountsRequest.Merge(m, src) +} +func (m *QueryModuleAccountsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryModuleAccountsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryModuleAccountsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryModuleAccountsRequest proto.InternalMessageInfo + +// QueryModuleAccountsResponse is the response type for the Query/ModuleAccounts RPC method. +type QueryModuleAccountsResponse struct { + Accounts []*types.Any `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts,omitempty"` +} + +func (m *QueryModuleAccountsResponse) Reset() { *m = QueryModuleAccountsResponse{} } +func (m *QueryModuleAccountsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryModuleAccountsResponse) ProtoMessage() {} +func (*QueryModuleAccountsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c451370b3929a27c, []int{7} +} +func (m *QueryModuleAccountsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryModuleAccountsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryModuleAccountsResponse.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 *QueryModuleAccountsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryModuleAccountsResponse.Merge(m, src) +} +func (m *QueryModuleAccountsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryModuleAccountsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryModuleAccountsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryModuleAccountsResponse proto.InternalMessageInfo + +func (m *QueryModuleAccountsResponse) GetAccounts() []*types.Any { + if m != nil { + return m.Accounts + } + return nil +} + func init() { proto.RegisterType((*QueryAccountsRequest)(nil), "cosmos.auth.v1beta1.QueryAccountsRequest") proto.RegisterType((*QueryAccountsResponse)(nil), "cosmos.auth.v1beta1.QueryAccountsResponse") @@ -308,46 +390,52 @@ func init() { proto.RegisterType((*QueryAccountResponse)(nil), "cosmos.auth.v1beta1.QueryAccountResponse") proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.auth.v1beta1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.auth.v1beta1.QueryParamsResponse") + proto.RegisterType((*QueryModuleAccountsRequest)(nil), "cosmos.auth.v1beta1.QueryModuleAccountsRequest") + proto.RegisterType((*QueryModuleAccountsResponse)(nil), "cosmos.auth.v1beta1.QueryModuleAccountsResponse") } func init() { proto.RegisterFile("cosmos/auth/v1beta1/query.proto", fileDescriptor_c451370b3929a27c) } var fileDescriptor_c451370b3929a27c = []byte{ - // 537 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x6b, 0x13, 0x4f, - 0x18, 0xc6, 0x77, 0xda, 0xff, 0x3f, 0x89, 0x53, 0x4f, 0xd3, 0x08, 0x71, 0x6b, 0x77, 0xcb, 0x8a, - 0x26, 0x29, 0x74, 0x86, 0xc6, 0x53, 0x45, 0x84, 0x46, 0x50, 0xbc, 0xc5, 0xc5, 0x93, 0x07, 0x65, - 0x36, 0x19, 0xb7, 0x41, 0xb3, 0xb3, 0xcd, 0xec, 0x8a, 0x41, 0x04, 0xf1, 0xd4, 0x9b, 0x82, 0x5f, - 0x20, 0x37, 0xbf, 0x80, 0x1f, 0xa2, 0x78, 0x2a, 0x78, 0xf1, 0x24, 0x92, 0x78, 0xf0, 0x63, 0x48, - 0x66, 0xde, 0x89, 0x8d, 0xac, 0x26, 0xa7, 0xdd, 0x99, 0x79, 0x9f, 0xe7, 0xf9, 0xbd, 0xef, 0x0c, - 0xf6, 0xbb, 0x52, 0x0d, 0xa4, 0x62, 0x3c, 0xcf, 0x8e, 0xd8, 0x8b, 0xfd, 0x48, 0x64, 0x7c, 0x9f, - 0x1d, 0xe7, 0x62, 0x38, 0xa2, 0xe9, 0x50, 0x66, 0x92, 0x6c, 0x9a, 0x02, 0x3a, 0x2b, 0xa0, 0x50, - 0xe0, 0xee, 0x82, 0x2a, 0xe2, 0x4a, 0x98, 0xea, 0xb9, 0x36, 0xe5, 0x71, 0x3f, 0xe1, 0x59, 0x5f, - 0x26, 0xc6, 0xc0, 0xad, 0xc6, 0x32, 0x96, 0xfa, 0x97, 0xcd, 0xfe, 0x60, 0xf7, 0x72, 0x2c, 0x65, - 0xfc, 0x5c, 0x30, 0xbd, 0x8a, 0xf2, 0xa7, 0x8c, 0x27, 0x90, 0xe8, 0x5e, 0x81, 0x23, 0x9e, 0xf6, - 0x19, 0x4f, 0x12, 0x99, 0x69, 0x37, 0x05, 0xa7, 0x5e, 0x11, 0xb0, 0x86, 0x03, 0x63, 0x73, 0xfe, - 0xc4, 0x24, 0x02, 0xbc, 0x5e, 0x04, 0x8f, 0x71, 0xf5, 0xc1, 0x8c, 0xf5, 0xb0, 0xdb, 0x95, 0x79, - 0x92, 0xa9, 0x50, 0x1c, 0xe7, 0x42, 0x65, 0xe4, 0x2e, 0xc6, 0xbf, 0xa9, 0x6b, 0x68, 0x07, 0x35, - 0x36, 0x5a, 0xd7, 0x29, 0x48, 0x67, 0x2d, 0x52, 0x33, 0x10, 0x48, 0xa3, 0x1d, 0x1e, 0x0b, 0xd0, - 0x86, 0xe7, 0x94, 0xc1, 0x18, 0xe1, 0x4b, 0x7f, 0x04, 0xa8, 0x54, 0x26, 0x4a, 0x90, 0xdb, 0xb8, - 0xc2, 0x61, 0xaf, 0x86, 0x76, 0xd6, 0x1b, 0x1b, 0xad, 0x2a, 0x35, 0x5d, 0x52, 0x3b, 0x00, 0x7a, - 0x98, 0x8c, 0xda, 0x17, 0x3f, 0x7f, 0xda, 0xab, 0x80, 0xfa, 0x7e, 0x38, 0xd7, 0x90, 0x7b, 0x0b, - 0x84, 0x6b, 0x9a, 0xb0, 0xbe, 0x94, 0xd0, 0x84, 0x2f, 0x20, 0x1e, 0xe0, 0xcd, 0xf3, 0x84, 0x76, - 0x02, 0x35, 0x5c, 0xe6, 0xbd, 0xde, 0x50, 0x28, 0xa5, 0xdb, 0xbf, 0x10, 0xda, 0xe5, 0xcd, 0xca, - 0xc9, 0xd8, 0x77, 0x7e, 0x8e, 0x7d, 0x27, 0x78, 0xb8, 0x38, 0xbd, 0x79, 0x6f, 0xb7, 0x70, 0x19, - 0x38, 0x61, 0x74, 0xab, 0xb4, 0x66, 0x25, 0x41, 0x15, 0x13, 0xed, 0xda, 0xe1, 0x43, 0x3e, 0xb0, - 0x37, 0x12, 0x74, 0x00, 0xd3, 0xee, 0x42, 0xd4, 0x01, 0x2e, 0xa5, 0x7a, 0x07, 0x92, 0xb6, 0x68, - 0xc1, 0xe3, 0xa4, 0x46, 0xd4, 0xfe, 0xef, 0xf4, 0x9b, 0xef, 0x84, 0x20, 0x68, 0x7d, 0x5c, 0xc7, - 0xff, 0x6b, 0x4b, 0x72, 0x82, 0xb0, 0xe5, 0x50, 0xa4, 0x59, 0xe8, 0x50, 0xf4, 0x4a, 0xdc, 0xdd, - 0x55, 0x4a, 0x0d, 0x68, 0x70, 0xed, 0xed, 0x97, 0x1f, 0x1f, 0xd6, 0x7c, 0xb2, 0xcd, 0x0a, 0x5f, - 0xab, 0x4d, 0x7f, 0x87, 0x70, 0x19, 0xb4, 0xa4, 0xb1, 0xd4, 0xde, 0x82, 0x34, 0x57, 0xa8, 0x04, - 0x0e, 0xa6, 0x39, 0x9a, 0xa4, 0xfe, 0x4f, 0x0e, 0xf6, 0x0a, 0x6e, 0xfb, 0x35, 0x79, 0x83, 0x70, - 0xc9, 0xcc, 0x8f, 0xd4, 0xff, 0x1e, 0xb3, 0x70, 0x59, 0x6e, 0x63, 0x79, 0x21, 0xe0, 0x5c, 0xd5, - 0x38, 0xdb, 0x64, 0xab, 0x10, 0xc7, 0xdc, 0x54, 0xfb, 0xce, 0xe9, 0xc4, 0x43, 0x67, 0x13, 0x0f, - 0x7d, 0x9f, 0x78, 0xe8, 0xfd, 0xd4, 0x73, 0xce, 0xa6, 0x9e, 0xf3, 0x75, 0xea, 0x39, 0x8f, 0x9a, - 0x71, 0x3f, 0x3b, 0xca, 0x23, 0xda, 0x95, 0x03, 0x6b, 0x60, 0x3e, 0x7b, 0xaa, 0xf7, 0x8c, 0xbd, - 0x34, 0x6e, 0xd9, 0x28, 0x15, 0x2a, 0x2a, 0xe9, 0xb7, 0x77, 0xe3, 0x57, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x2a, 0xe1, 0x81, 0xd3, 0xdf, 0x04, 0x00, 0x00, + // 602 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x4f, 0x6b, 0x13, 0x41, + 0x18, 0xc6, 0x77, 0x6b, 0x6d, 0xeb, 0x54, 0x3c, 0x4c, 0x23, 0xc4, 0x4d, 0xbb, 0x29, 0xab, 0x36, + 0x49, 0x31, 0x33, 0x36, 0x9e, 0x2a, 0x22, 0x34, 0x8a, 0xe2, 0x41, 0x88, 0xc1, 0x93, 0x07, 0xcb, + 0x24, 0x19, 0xb7, 0xc1, 0x66, 0x67, 0x9b, 0xd9, 0x15, 0x83, 0x08, 0xe2, 0xa9, 0x37, 0x05, 0xbf, + 0x40, 0xfc, 0x0e, 0xfd, 0x10, 0xa5, 0x5e, 0x0a, 0x5e, 0x3c, 0x89, 0x24, 0x1e, 0xfc, 0x18, 0x92, + 0x99, 0x77, 0x62, 0xb6, 0x6c, 0x4d, 0x4e, 0xd9, 0x99, 0x79, 0x9f, 0xf7, 0xf9, 0xbd, 0x7f, 0x82, + 0xf2, 0x4d, 0x21, 0x3b, 0x42, 0x52, 0x16, 0x47, 0x7b, 0xf4, 0xcd, 0x56, 0x83, 0x47, 0x6c, 0x8b, + 0x1e, 0xc4, 0xbc, 0xdb, 0x23, 0x61, 0x57, 0x44, 0x02, 0xaf, 0xe8, 0x00, 0x32, 0x0a, 0x20, 0x10, + 0xe0, 0x6c, 0x82, 0xaa, 0xc1, 0x24, 0xd7, 0xd1, 0x63, 0x6d, 0xc8, 0xfc, 0x76, 0xc0, 0xa2, 0xb6, + 0x08, 0x74, 0x02, 0x27, 0xe3, 0x0b, 0x5f, 0xa8, 0x4f, 0x3a, 0xfa, 0x82, 0xdb, 0x6b, 0xbe, 0x10, + 0xfe, 0x3e, 0xa7, 0xea, 0xd4, 0x88, 0x5f, 0x51, 0x16, 0x80, 0xa3, 0xb3, 0x0a, 0x4f, 0x2c, 0x6c, + 0x53, 0x16, 0x04, 0x22, 0x52, 0xd9, 0x24, 0xbc, 0xba, 0x69, 0xc0, 0x0a, 0x0e, 0x12, 0xeb, 0xf7, + 0x5d, 0xed, 0x08, 0xf0, 0xea, 0xe0, 0xbd, 0x44, 0x99, 0x67, 0x23, 0xd6, 0x9d, 0x66, 0x53, 0xc4, + 0x41, 0x24, 0xeb, 0xfc, 0x20, 0xe6, 0x32, 0xc2, 0x8f, 0x10, 0xfa, 0x47, 0x9d, 0xb5, 0xd7, 0xed, + 0xe2, 0x72, 0x65, 0x83, 0x80, 0x74, 0x54, 0x22, 0xd1, 0x0d, 0x01, 0x37, 0x52, 0x63, 0x3e, 0x07, + 0x6d, 0x7d, 0x42, 0xe9, 0xf5, 0x6d, 0x74, 0xf5, 0x8c, 0x81, 0x0c, 0x45, 0x20, 0x39, 0xbe, 0x8f, + 0x96, 0x18, 0xdc, 0x65, 0xed, 0xf5, 0x0b, 0xc5, 0xe5, 0x4a, 0x86, 0xe8, 0x2a, 0x89, 0x69, 0x00, + 0xd9, 0x09, 0x7a, 0xd5, 0xcb, 0x27, 0x47, 0xe5, 0x25, 0x50, 0x3f, 0xa9, 0x8f, 0x35, 0xf8, 0x71, + 0x82, 0x70, 0x4e, 0x11, 0x16, 0xa6, 0x12, 0x6a, 0xf3, 0x04, 0xe2, 0x36, 0x5a, 0x99, 0x24, 0x34, + 0x1d, 0xc8, 0xa2, 0x45, 0xd6, 0x6a, 0x75, 0xb9, 0x94, 0xaa, 0xfc, 0x4b, 0x75, 0x73, 0xbc, 0xbb, + 0x74, 0xd8, 0xcf, 0x5b, 0x7f, 0xfa, 0x79, 0xcb, 0x7b, 0x9e, 0xec, 0xde, 0xb8, 0xb6, 0x7b, 0x68, + 0x11, 0x38, 0xa1, 0x75, 0xb3, 0x94, 0x66, 0x24, 0x5e, 0x06, 0x61, 0x95, 0xb5, 0xc6, 0xba, 0xac, + 0x63, 0x26, 0xe2, 0xd5, 0x00, 0xd3, 0xdc, 0x82, 0xd5, 0x36, 0x5a, 0x08, 0xd5, 0x0d, 0x38, 0xe5, + 0x48, 0xca, 0x72, 0x12, 0x2d, 0xaa, 0xce, 0x1f, 0xff, 0xcc, 0x5b, 0x75, 0x10, 0x78, 0xab, 0xc8, + 0x51, 0x19, 0x9f, 0x8a, 0x56, 0xbc, 0xcf, 0xcf, 0x6c, 0x80, 0xd7, 0x44, 0xb9, 0xd4, 0x57, 0xf0, + 0x7d, 0x38, 0xe3, 0xf8, 0xf0, 0xc9, 0x51, 0xf9, 0x4a, 0x22, 0xc7, 0xc4, 0x10, 0x2b, 0xdf, 0xe6, + 0xd1, 0x45, 0xe5, 0x82, 0x0f, 0x6d, 0x64, 0x5a, 0x21, 0x71, 0x29, 0xb5, 0x88, 0xb4, 0x45, 0x75, + 0x36, 0x67, 0x09, 0xd5, 0xcc, 0xde, 0xcd, 0x8f, 0xdf, 0x7f, 0x7f, 0x99, 0xcb, 0xe3, 0x35, 0x9a, + 0xfa, 0x87, 0x31, 0xee, 0x9f, 0x6c, 0xb4, 0x08, 0x5a, 0x5c, 0x9c, 0x9a, 0xde, 0x80, 0x94, 0x66, + 0x88, 0x04, 0x0e, 0xaa, 0x38, 0x4a, 0xb8, 0xf0, 0x5f, 0x0e, 0xfa, 0x0e, 0x16, 0xee, 0x3d, 0xfe, + 0x60, 0xa3, 0x05, 0x3d, 0x42, 0x5c, 0x38, 0xdf, 0x26, 0xb1, 0x2f, 0x4e, 0x71, 0x7a, 0x20, 0xe0, + 0x5c, 0x57, 0x38, 0x6b, 0x38, 0x97, 0x8a, 0xa3, 0x97, 0x05, 0x7f, 0xb5, 0x51, 0x72, 0x8c, 0x12, + 0xd3, 0xf3, 0x1d, 0x52, 0x57, 0xca, 0xb9, 0x3d, 0xbb, 0x00, 0xd0, 0x6e, 0x29, 0xb4, 0x0d, 0x7c, + 0x23, 0x15, 0xad, 0xa3, 0x44, 0xbb, 0xa6, 0x61, 0xd5, 0x07, 0xc7, 0x03, 0xd7, 0x3e, 0x1d, 0xb8, + 0xf6, 0xaf, 0x81, 0x6b, 0x7f, 0x1e, 0xba, 0xd6, 0xe9, 0xd0, 0xb5, 0x7e, 0x0c, 0x5d, 0xeb, 0x45, + 0xc9, 0x6f, 0x47, 0x7b, 0x71, 0x83, 0x34, 0x45, 0xc7, 0x64, 0xd2, 0x3f, 0x65, 0xd9, 0x7a, 0x4d, + 0xdf, 0xea, 0xb4, 0x51, 0x2f, 0xe4, 0xb2, 0xb1, 0xa0, 0xd6, 0xf7, 0xce, 0xdf, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xc0, 0xd9, 0x5e, 0x89, 0x06, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -368,6 +456,8 @@ type QueryClient interface { Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) // Params queries all parameters. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // ModuleAccounts returns all the existing Module Accounts. + ModuleAccounts(ctx context.Context, in *QueryModuleAccountsRequest, opts ...grpc.CallOption) (*QueryModuleAccountsResponse, error) } type queryClient struct { @@ -405,6 +495,15 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . return out, nil } +func (c *queryClient) ModuleAccounts(ctx context.Context, in *QueryModuleAccountsRequest, opts ...grpc.CallOption) (*QueryModuleAccountsResponse, error) { + out := new(QueryModuleAccountsResponse) + err := c.cc.Invoke(ctx, "/cosmos.auth.v1beta1.Query/ModuleAccounts", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Accounts returns all the existing accounts @@ -413,6 +512,8 @@ type QueryServer interface { Account(context.Context, *QueryAccountRequest) (*QueryAccountResponse, error) // Params queries all parameters. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // ModuleAccounts returns all the existing Module Accounts. + ModuleAccounts(context.Context, *QueryModuleAccountsRequest) (*QueryModuleAccountsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -428,6 +529,9 @@ func (*UnimplementedQueryServer) Account(ctx context.Context, req *QueryAccountR func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } +func (*UnimplementedQueryServer) ModuleAccounts(ctx context.Context, req *QueryModuleAccountsRequest) (*QueryModuleAccountsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ModuleAccounts not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -487,6 +591,24 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Query_ModuleAccounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryModuleAccountsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ModuleAccounts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.auth.v1beta1.Query/ModuleAccounts", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ModuleAccounts(ctx, req.(*QueryModuleAccountsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.auth.v1beta1.Query", HandlerType: (*QueryServer)(nil), @@ -503,6 +625,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Params", Handler: _Query_Params_Handler, }, + { + MethodName: "ModuleAccounts", + Handler: _Query_ModuleAccounts_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cosmos/auth/v1beta1/query.proto", @@ -713,6 +839,66 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QueryModuleAccountsRequest) 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 *QueryModuleAccountsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryModuleAccountsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryModuleAccountsResponse) 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 *QueryModuleAccountsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryModuleAccountsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Accounts) > 0 { + for iNdEx := len(m.Accounts) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Accounts[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -802,6 +988,30 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QueryModuleAccountsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryModuleAccountsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Accounts) > 0 { + for _, e := range m.Accounts { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1315,6 +1525,140 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryModuleAccountsRequest) 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 ErrIntOverflowQuery + } + 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: QueryModuleAccountsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryModuleAccountsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryModuleAccountsResponse) 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 ErrIntOverflowQuery + } + 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: QueryModuleAccountsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryModuleAccountsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Accounts", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Accounts = append(m.Accounts, &types.Any{}) + if err := m.Accounts[len(m.Accounts)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/auth/types/query.pb.gw.go b/x/auth/types/query.pb.gw.go index f6d93f2924f2..73ff335f0bda 100644 --- a/x/auth/types/query.pb.gw.go +++ b/x/auth/types/query.pb.gw.go @@ -139,6 +139,24 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal } +func request_Query_ModuleAccounts_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryModuleAccountsRequest + var metadata runtime.ServerMetadata + + msg, err := client.ModuleAccounts(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ModuleAccounts_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryModuleAccountsRequest + var metadata runtime.ServerMetadata + + msg, err := server.ModuleAccounts(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -205,6 +223,26 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ModuleAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ModuleAccounts_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ModuleAccounts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -306,6 +344,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ModuleAccounts_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ModuleAccounts_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ModuleAccounts_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -315,6 +373,8 @@ var ( pattern_Query_Account_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"cosmos", "auth", "v1beta1", "accounts", "address"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ModuleAccounts_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "auth", "v1beta1", "module_accounts"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -323,4 +383,6 @@ var ( forward_Query_Account_0 = runtime.ForwardResponseMessage forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_ModuleAccounts_0 = runtime.ForwardResponseMessage ) From bc201f8054049f03f201d27e1ed72a01a12196d6 Mon Sep 17 00:00:00 2001 From: iczc Date: Tue, 3 Aug 2021 21:46:16 +0800 Subject: [PATCH 05/31] build: Update Dockerfile (#9839) --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index af8b4c1febce..ffcbeccbc15f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ # TODO: demo connecting rest-server (or is this in server now?) FROM golang:alpine AS build-env -# Install minimum necessary dependencies, +# Install minimum necessary dependencies ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 RUN apk add --no-cache $PACKAGES From 40d22c7ab3a385bde480ce0b10d9920d8646d67e Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 3 Aug 2021 19:43:38 +0200 Subject: [PATCH 06/31] refactoring balance.coin validation --- x/bank/types/balance.go | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/x/bank/types/balance.go b/x/bank/types/balance.go index 2aa7967d5060..78539ace6cff 100644 --- a/x/bank/types/balance.go +++ b/x/bank/types/balance.go @@ -30,38 +30,12 @@ func (b Balance) GetCoins() sdk.Coins { // Validate checks for address and coins correctness. func (b Balance) Validate() error { - _, err := sdk.AccAddressFromBech32(b.Address) - if err != nil { + if _, err := sdk.AccAddressFromBech32(b.Address); err != nil { return err } - var prevDenom string - if !b.Coins.Empty() { - prevDenom = b.Coins[0].Denom - } - seenDenoms := make(map[string]bool) - - // NOTE: we perform a custom validation since the coins.Validate function - // errors on zero balance coins - for _, coin := range b.Coins { - if seenDenoms[coin.Denom] { - return fmt.Errorf("duplicate denomination %s", coin.Denom) - } - - if err := sdk.ValidateDenom(coin.Denom); err != nil { - return err - } - - if coin.Denom < prevDenom { - return fmt.Errorf("denomination %s is not sorted", coin.Denom) - } - - if coin.IsNegative() { - return fmt.Errorf("coin %s amount is cannot be negative", coin.Denom) - } - - seenDenoms[coin.Denom] = true - prevDenom = coin.Denom + if err := b.Coins.Validate(); err != nil { + return err } return nil From 2468b64cffd889e626ec6b7e047459ed39cafd58 Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Tue, 3 Aug 2021 19:44:03 +0200 Subject: [PATCH 07/31] adding 0 value coin test case --- x/bank/types/balance_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/x/bank/types/balance_test.go b/x/bank/types/balance_test.go index 1c7d4255c76b..fd6b72a3a1de 100644 --- a/x/bank/types/balance_test.go +++ b/x/bank/types/balance_test.go @@ -64,6 +64,17 @@ func TestBalanceValidate(t *testing.T) { }, true, }, + { + "0 value coin", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.NewInt64Coin("atom", 0), + sdk.NewInt64Coin("zatom", 2), + }, + }, + true, + }, { "unsorted coins", bank.Balance{ From 7f316adb97706d82ac5de2bae608eb5ed42300ec Mon Sep 17 00:00:00 2001 From: Aditya Date: Tue, 3 Aug 2021 20:50:08 +0200 Subject: [PATCH 08/31] fix!: Capability Issue on Restart, Backport to v0.43 (#9836) ## Description Closes: #9800 The following is a breaking fix to #9800. In the non-breaking fix, the initialization logic was moved out of `InitializeAndSeal` but the API was preserved. I've now removed `InitializeAndSeal` and replaced it with just the `Seal` function, which may be called much more cleanly in `app.go` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 2 + simapp/app.go | 21 +++---- x/capability/abci.go | 21 +++++++ x/capability/capability_test.go | 97 ++++++++++++++++++++++++++++++ x/capability/genesis.go | 5 +- x/capability/genesis_test.go | 59 ++++++++++++++++++ x/capability/keeper/keeper.go | 74 +++++++++++------------ x/capability/keeper/keeper_test.go | 6 +- x/capability/module.go | 4 +- x/capability/types/keys.go | 3 + 10 files changed, 234 insertions(+), 58 deletions(-) create mode 100644 x/capability/abci.go create mode 100644 x/capability/capability_test.go create mode 100644 x/capability/genesis_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 6579ea3c70a6..a8f2738f449f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9432](https://github.com/cosmos/cosmos-sdk/pull/9432) `ConsensusParamsKeyTable` moved from `params/keeper` to `params/types` * [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled * [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules. +* (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. ### Client Breaking Changes @@ -94,6 +95,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. * [\#9762](https://github.com/cosmos/cosmos-sdk/pull/9762) The init command uses the chain-id from the client config if --chain-id is not provided * [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. +* [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Fixes capability initialization issue on tx revert by moving initialization logic to `InitChain` and `BeginBlock`. ### State Machine Breaking diff --git a/simapp/app.go b/simapp/app.go index 4ce4c791afb5..c1cfce132cf1 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -13,7 +13,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" "github.com/cosmos/cosmos-sdk/baseapp" @@ -208,7 +207,9 @@ func NewSimApp( authzkeeper.StoreKey, ) tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey) - memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) + // NOTE: The testingkey is just mounted for testing purposes. Actual applications should + // not include this key. + memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, "testingkey") app := &SimApp{ BaseApp: bApp, @@ -227,6 +228,9 @@ func NewSimApp( bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable())) app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey]) + // Applications that wish to enforce statically created ScopedKeepers should call `Seal` after creating + // their scoped modules in `NewApp` with `ScopeToModule` + app.CapabilityKeeper.Seal() // add keepers app.AccountKeeper = authkeeper.NewAccountKeeper( @@ -322,8 +326,9 @@ func NewSimApp( // there is nothing left over in the validator fee pool, so as to keep the // CanWithdrawInvariant invariant. // NOTE: staking module is required if HistoricalEntries param > 0 + // NOTE: capability module's beginblocker must come before any modules using capabilities (e.g. IBC) app.mm.SetOrderBeginBlockers( - upgradetypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, + upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, stakingtypes.ModuleName, ) app.mm.SetOrderEndBlockers(crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName) @@ -399,16 +404,6 @@ func NewSimApp( if err := app.LoadLatestVersion(); err != nil { tmos.Exit(err.Error()) } - - // Initialize and seal the capability keeper so all persistent capabilities - // are loaded in-memory and prevent any further modules from creating scoped - // sub-keepers. - // This must be done during creation of baseapp rather than in InitChain so - // that in-memory capabilities get regenerated on app restart. - // Note that since this reads from the store, we can only perform it when - // `loadLatest` is set to true. - ctx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) - app.CapabilityKeeper.InitializeAndSeal(ctx) } return app diff --git a/x/capability/abci.go b/x/capability/abci.go new file mode 100644 index 000000000000..9d1c94b5b901 --- /dev/null +++ b/x/capability/abci.go @@ -0,0 +1,21 @@ +package capability + +import ( + "time" + + "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +// BeginBlocker will call InitMemStore to initialize the memory stores in the case +// that this is the first time the node is executing a block since restarting (wiping memory). +// In this case, the BeginBlocker method will reinitialize the memory stores locally, so that subsequent +// capability transactions will pass. +// Otherwise BeginBlocker performs a no-op. +func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + k.InitMemStore(ctx) +} diff --git a/x/capability/capability_test.go b/x/capability/capability_test.go new file mode 100644 index 000000000000..45a5f6ea42a8 --- /dev/null +++ b/x/capability/capability_test.go @@ -0,0 +1,97 @@ +package capability_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" +) + +type CapabilityTestSuite struct { + suite.Suite + + cdc codec.Codec + ctx sdk.Context + app *simapp.SimApp + keeper *keeper.Keeper + module module.AppModule +} + +func (suite *CapabilityTestSuite) SetupTest() { + checkTx := false + app := simapp.Setup(checkTx) + cdc := app.AppCodec() + + // create new keeper so we can define custom scoping before init and seal + keeper := keeper.NewKeeper(cdc, app.GetKey(types.StoreKey), app.GetMemKey(types.MemStoreKey)) + + suite.app = app + suite.ctx = app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) + suite.keeper = keeper + suite.cdc = cdc + suite.module = capability.NewAppModule(cdc, *keeper) +} + +// The following test case mocks a specific bug discovered in https://github.com/cosmos/cosmos-sdk/issues/9800 +// and ensures that the current code successfully fixes the issue. +func (suite *CapabilityTestSuite) TestInitializeMemStore() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + + cap1, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap1) + + // mock statesync by creating new keeper that shares persistent state but loses in-memory map + newKeeper := keeper.NewKeeper(suite.cdc, suite.app.GetKey(types.StoreKey), suite.app.GetMemKey("testingkey")) + newSk1 := newKeeper.ScopeToModule(banktypes.ModuleName) + + // Mock App startup + ctx := suite.app.BaseApp.NewUncachedContext(false, tmproto.Header{}) + newKeeper.Seal() + suite.Require().False(newKeeper.IsInitialized(ctx), "memstore initialized flag set before BeginBlock") + + // Mock app beginblock and ensure that no gas has been consumed and memstore is initialized + ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{}).WithBlockGasMeter(sdk.NewGasMeter(50)) + prevGas := ctx.BlockGasMeter().GasConsumed() + restartedModule := capability.NewAppModule(suite.cdc, *newKeeper) + restartedModule.BeginBlock(ctx, abci.RequestBeginBlock{}) + suite.Require().True(newKeeper.IsInitialized(ctx), "memstore initialized flag not set") + gasUsed := ctx.BlockGasMeter().GasConsumed() + + suite.Require().Equal(prevGas, gasUsed, "beginblocker consumed gas during execution") + + // Mock the first transaction getting capability and subsequently failing + // by using a cached context and discarding all cached writes. + cacheCtx, _ := ctx.CacheContext() + _, ok := newSk1.GetCapability(cacheCtx, "transfer") + suite.Require().True(ok) + + // Ensure that the second transaction can still receive capability even if first tx fails. + ctx = suite.app.BaseApp.NewContext(false, tmproto.Header{}) + + cap1, ok = newSk1.GetCapability(ctx, "transfer") + suite.Require().True(ok) + + // Ensure the capabilities don't get reinitialized on next BeginBlock + // by testing to see if capability returns same pointer + // also check that initialized flag is still set + restartedModule.BeginBlock(ctx, abci.RequestBeginBlock{}) + recap, ok := newSk1.GetCapability(ctx, "transfer") + suite.Require().True(ok) + suite.Require().Equal(cap1, recap, "capabilities got reinitialized after second BeginBlock") + suite.Require().True(newKeeper.IsInitialized(ctx), "memstore initialized flag not set") +} + +func TestCapabilityTestSuite(t *testing.T) { + suite.Run(t, new(CapabilityTestSuite)) +} diff --git a/x/capability/genesis.go b/x/capability/genesis.go index ba8e09dcd375..2e9a11b994be 100644 --- a/x/capability/genesis.go +++ b/x/capability/genesis.go @@ -13,11 +13,12 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) panic(err) } - // set owners for each index and initialize capability + // set owners for each index for _, genOwner := range genState.Owners { k.SetOwners(ctx, genOwner.Index, genOwner.IndexOwners) - k.InitializeCapability(ctx, genOwner.Index, genOwner.IndexOwners) } + // initialize in-memory capabilities + k.InitMemStore(ctx) } // ExportGenesis returns the capability module's exported genesis. diff --git a/x/capability/genesis_test.go b/x/capability/genesis_test.go new file mode 100644 index 000000000000..34a09960e750 --- /dev/null +++ b/x/capability/genesis_test.go @@ -0,0 +1,59 @@ +package capability_test + +import ( + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/capability/keeper" + "github.com/cosmos/cosmos-sdk/x/capability/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" +) + +func (suite *CapabilityTestSuite) TestGenesis() { + sk1 := suite.keeper.ScopeToModule(banktypes.ModuleName) + sk2 := suite.keeper.ScopeToModule(stakingtypes.ModuleName) + + cap1, err := sk1.NewCapability(suite.ctx, "transfer") + suite.Require().NoError(err) + suite.Require().NotNil(cap1) + + err = sk2.ClaimCapability(suite.ctx, cap1, "transfer") + suite.Require().NoError(err) + + cap2, err := sk2.NewCapability(suite.ctx, "ica") + suite.Require().NoError(err) + suite.Require().NotNil(cap2) + + genState := capability.ExportGenesis(suite.ctx, *suite.keeper) + + // create new app that does not share persistent or in-memory state + // and initialize app from exported genesis state above. + db := dbm.NewMemDB() + encCdc := simapp.MakeTestEncodingConfig() + newApp := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + + newKeeper := keeper.NewKeeper(suite.cdc, newApp.GetKey(types.StoreKey), newApp.GetMemKey(types.MemStoreKey)) + newSk1 := newKeeper.ScopeToModule(banktypes.ModuleName) + newSk2 := newKeeper.ScopeToModule(stakingtypes.ModuleName) + deliverCtx, _ := newApp.BaseApp.NewUncachedContext(false, tmproto.Header{}).WithBlockGasMeter(sdk.NewInfiniteGasMeter()).CacheContext() + + capability.InitGenesis(deliverCtx, *newKeeper, *genState) + + // check that all previous capabilities exist in new app after InitGenesis + sk1Cap1, ok := newSk1.GetCapability(deliverCtx, "transfer") + suite.Require().True(ok, "could not get first capability after genesis on first ScopedKeeper") + suite.Require().Equal(*cap1, *sk1Cap1, "capability values not equal on first ScopedKeeper") + + sk2Cap1, ok := newSk2.GetCapability(deliverCtx, "transfer") + suite.Require().True(ok, "could not get first capability after genesis on first ScopedKeeper") + suite.Require().Equal(*cap1, *sk2Cap1, "capability values not equal on first ScopedKeeper") + + sk2Cap2, ok := newSk2.GetCapability(deliverCtx, "ica") + suite.Require().True(ok, "could not get second capability after genesis on second ScopedKeeper") + suite.Require().Equal(*cap2, *sk2Cap2, "capability values not equal on second ScopedKeeper") +} diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index ead46dd1f4f9..48e0bdfe3184 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -13,14 +13,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/capability/types" ) -// initialized is a global variable used by GetCapability to ensure that the memory store -// and capability map are correctly populated. A state-synced node may copy over all the persistent -// state and start running the application without having the in-memory state required for x/capability. -// Thus, we must initialized the memory stores on-the-fly during tx execution once the first GetCapability -// is called. -// This is a temporary fix and should be replaced by a more robust solution in the next breaking release. -var initialized = false - type ( // Keeper defines the capability module's keeper. It is responsible for provisioning, // tracking, and authenticating capabilities at runtime. During application @@ -97,15 +89,21 @@ func (k *Keeper) ScopeToModule(moduleName string) ScopedKeeper { } } -// InitializeAndSeal loads all capabilities from the persistent KVStore into the -// in-memory store and seals the keeper to prevent further modules from creating -// a scoped keeper. InitializeAndSeal must be called once after the application -// state is loaded. -func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { +// Seal seals the keeper to prevent further modules from creating a scoped keeper. +// Seal may be called during app initialization for applications that do not wish to create scoped keepers dynamically. +func (k *Keeper) Seal() { if k.sealed { panic("cannot initialize and seal an already sealed capability keeper") } + k.sealed = true +} + +// InitMemStore will initialize the memory store if the initialized flag is not set. +// InitMemStore logic should be run in first `BeginBlock` or `InitChain` (whichever is run on app start) +// so that the memory store is properly initialized before any transactions are run. +// InitMemStore also asserts the memory store type is correct, and will panic if it does not have store type of `StoreTypeMemory`. +func (k *Keeper) InitMemStore(ctx sdk.Context) { memStore := ctx.KVStore(k.memKey) memStoreType := memStore.GetStoreType() @@ -113,22 +111,36 @@ func (k *Keeper) InitializeAndSeal(ctx sdk.Context) { panic(fmt.Sprintf("invalid memory store type; got %s, expected: %s", memStoreType, sdk.StoreTypeMemory)) } - prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) - iterator := sdk.KVStorePrefixIterator(prefixStore, nil) + // create context with no block gas meter to ensure we do not consume gas during local initialization logic. + noGasCtx := ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) + + // check if memory store has not been initialized yet by checking if initialized flag is nil. + if !k.IsInitialized(noGasCtx) { + prefixStore := prefix.NewStore(noGasCtx.KVStore(k.storeKey), types.KeyPrefixIndexCapability) + iterator := sdk.KVStorePrefixIterator(prefixStore, nil) + + // initialize the in-memory store for all persisted capabilities + defer iterator.Close() - // initialize the in-memory store for all persisted capabilities - defer iterator.Close() + for ; iterator.Valid(); iterator.Next() { + index := types.IndexFromKey(iterator.Key()) - for ; iterator.Valid(); iterator.Next() { - index := types.IndexFromKey(iterator.Key()) + var capOwners types.CapabilityOwners - var capOwners types.CapabilityOwners + k.cdc.MustUnmarshal(iterator.Value(), &capOwners) + k.InitializeCapability(noGasCtx, index, capOwners) + } - k.cdc.MustUnmarshal(iterator.Value(), &capOwners) - k.InitializeCapability(ctx, index, capOwners) + // set the initialized flag so we don't rerun initialization logic + memStore := noGasCtx.KVStore(k.memKey) + memStore.Set(types.KeyMemInitialized, []byte{1}) } +} - k.sealed = true +// IsInitialized returns true if the initialized flag is set, and false otherwise +func (k *Keeper) IsInitialized(ctx sdk.Context) bool { + memStore := ctx.KVStore(k.memKey) + return memStore.Get(types.KeyMemInitialized) != nil } // InitializeIndex sets the index to one (or greater) in InitChain according @@ -350,22 +362,6 @@ func (sk ScopedKeeper) ReleaseCapability(ctx sdk.Context, cap *types.Capability) // by name. The module is not allowed to retrieve capabilities which it does not // own. func (sk ScopedKeeper) GetCapability(ctx sdk.Context, name string) (*types.Capability, bool) { - // Create a keeper that will set all in-memory mappings correctly into memstore and capmap if scoped keeper is not initialized yet. - // This ensures that the in-memory mappings are correctly filled in, in case this is a state-synced node. - // This is a temporary non-breaking fix, a future PR should store the reverse mapping in the persistent store and reconstruct forward mapping and capmap on the fly. - if !initialized { - // create context with infinite gas meter to avoid app state mismatch. - initCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - k := Keeper{ - cdc: sk.cdc, - storeKey: sk.storeKey, - memKey: sk.memKey, - capMap: sk.capMap, - } - k.InitializeAndSeal(initCtx) - initialized = true - } - if strings.TrimSpace(name) == "" { return nil, false } diff --git a/x/capability/keeper/keeper_test.go b/x/capability/keeper/keeper_test.go index 2868fd1d9b8b..e7b9b2d4a32d 100644 --- a/x/capability/keeper/keeper_test.go +++ b/x/capability/keeper/keeper_test.go @@ -36,7 +36,7 @@ func (suite *KeeperTestSuite) SetupTest() { suite.keeper = keeper } -func (suite *KeeperTestSuite) TestInitializeAndSeal() { +func (suite *KeeperTestSuite) TestSeal() { sk := suite.keeper.ScopeToModule(banktypes.ModuleName) suite.Require().Panics(func() { suite.keeper.ScopeToModule(" ") @@ -56,7 +56,7 @@ func (suite *KeeperTestSuite) TestInitializeAndSeal() { } suite.Require().NotPanics(func() { - suite.keeper.InitializeAndSeal(suite.ctx) + suite.keeper.Seal() }) for i, cap := range caps { @@ -67,7 +67,7 @@ func (suite *KeeperTestSuite) TestInitializeAndSeal() { } suite.Require().Panics(func() { - suite.keeper.InitializeAndSeal(suite.ctx) + suite.keeper.Seal() }) suite.Require().Panics(func() { diff --git a/x/capability/module.go b/x/capability/module.go index 9d298c26e0b1..5d6e8ffcbb03 100644 --- a/x/capability/module.go +++ b/x/capability/module.go @@ -143,7 +143,9 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. -func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { + BeginBlocker(ctx, am.keeper) +} // EndBlock executes all ABCI EndBlock logic respective to the capability module. It // returns no validator updates. diff --git a/x/capability/types/keys.go b/x/capability/types/keys.go index 5f171e28a7f4..aefd13ba2286 100644 --- a/x/capability/types/keys.go +++ b/x/capability/types/keys.go @@ -25,6 +25,9 @@ var ( // KeyPrefixIndexCapability defines a key prefix that stores index to capability // name mappings. KeyPrefixIndexCapability = []byte("capability_index") + + // KeyMemInitialized defines the key that stores the initialized flag in the memory store + KeyMemInitialized = []byte("mem_initialized") ) // RevCapabilityKey returns a reverse lookup key for a given module and capability From 18eea3a7c8dca75278a9c3ef9b7e1247ad9a357b Mon Sep 17 00:00:00 2001 From: Slav Keremidchiev Date: Wed, 4 Aug 2021 12:05:55 +0300 Subject: [PATCH 09/31] fix: fixed missing coin denomination in msg server logs (#9786) ## Description Closes: [#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) This PR modifies _x/staking/keeper/msg_server.go_ file and fixes coin denomination issues in the emitted events. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [x] reviewed state machine logic - [x] reviewed API design and naming - [x] reviewed documentation is accurate - [x] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 2 +- x/staking/client/testutil/suite.go | 9 +++++++++ x/staking/keeper/msg_server.go | 8 ++++---- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f2738f449f..032a3b5a85a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,7 +69,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints. * [\#9781](https://github.com/cosmos/cosmos-sdk/pull/9781) Improve`withdraw-all-rewards` UX when broadcast mode `async` or `async` is used. - +* [\#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) Missing coin denomination in logs ### CLI Breaking Changes diff --git a/x/staking/client/testutil/suite.go b/x/staking/client/testutil/suite.go index 4cbae20eba43..21679d01d16d 100644 --- a/x/staking/client/testutil/suite.go +++ b/x/staking/client/testutil/suite.go @@ -212,6 +212,15 @@ func (s *IntegrationTestSuite) TestNewCreateValidatorCmd() { txResp := tc.respType.(*sdk.TxResponse) require.Equal(tc.expectedCode, txResp.Code, "test: %s, output\n:", tc.name, out.String()) + + events := txResp.Logs[0].GetEvents() + for i := 0; i < len(events); i++ { + if events[i].GetType() == "create_validator" { + attributes := events[i].GetAttributes() + require.Equal(attributes[1].Value, "100stake") + break + } + } } }) } diff --git a/x/staking/keeper/msg_server.go b/x/staking/keeper/msg_server.go index db084531ca65..e32497a0dbf3 100644 --- a/x/staking/keeper/msg_server.go +++ b/x/staking/keeper/msg_server.go @@ -112,7 +112,7 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa sdk.NewEvent( types.EventTypeCreateValidator, sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.String()), ), sdk.NewEvent( sdk.EventTypeMessage, @@ -235,7 +235,7 @@ func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*typ sdk.NewEvent( types.EventTypeDelegate, sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()), ), sdk.NewEvent( @@ -301,7 +301,7 @@ func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRed types.EventTypeRedelegate, sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress), sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), ), sdk.NewEvent( @@ -362,7 +362,7 @@ func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) ( sdk.NewEvent( types.EventTypeUnbond, sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress), - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)), ), sdk.NewEvent( From f27c7ceef9eb6d0986db3a364140b4e75f1c45d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Aug 2021 11:50:02 +0200 Subject: [PATCH 10/31] build(deps): bump technote-space/get-diff-action from 4 to 5 (#9834) Bumps [technote-space/get-diff-action](https://github.com/technote-space/get-diff-action) from 4 to 5. - [Release notes](https://github.com/technote-space/get-diff-action/releases) - [Changelog](https://github.com/technote-space/get-diff-action/blob/main/.releasegarc) - [Commits](https://github.com/technote-space/get-diff-action/compare/v4...v5) --- updated-dependencies: - dependency-name: technote-space/get-diff-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/atlas.yml | 6 +++--- .github/workflows/lint.yml | 2 +- .github/workflows/proto.yml | 4 ++-- .github/workflows/sims.yml | 8 ++++---- .github/workflows/test.yml | 14 +++++++------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/workflows/atlas.yml b/.github/workflows/atlas.yml index 25e749439dab..861ef8110077 100644 --- a/.github/workflows/atlas.yml +++ b/.github/workflows/atlas.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | @@ -30,7 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e49fa0bce8c4..d6910cff8240 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 6 steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.go diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml index 503bed0c43cd..4ec3b07b2aec 100644 --- a/.github/workflows/proto.yml +++ b/.github/workflows/proto.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 5 steps: - uses: actions/checkout@master - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.proto @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@master - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.proto diff --git a/.github/workflows/sims.yml b/.github/workflows/sims.yml index f239c877f050..0af2b27e8aa2 100644 --- a/.github/workflows/sims.yml +++ b/.github/workflows/sims.yml @@ -46,7 +46,7 @@ jobs: go-version: 1.16 - name: Display go version run: go version - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.go @@ -72,7 +72,7 @@ jobs: go-version: 1.16 - name: Display go version run: go version - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: SUFFIX_FILTER: | **/**.go @@ -100,7 +100,7 @@ jobs: go-version: 1.16 - name: Display go version run: go version - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: SUFFIX_FILTER: | **/**.go @@ -128,7 +128,7 @@ jobs: go-version: 1.16 - name: Display go version run: go version - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: SUFFIX_FILTER: | **/**.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ff96995e028a..571e08688df7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-go@v2.1.3 with: go-version: 1.16 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | @@ -52,7 +52,7 @@ jobs: go-version: 1.16 - name: Display go version run: go version - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PREFIX_FILTER: | @@ -103,7 +103,7 @@ jobs: - uses: actions/setup-go@v2.1.3 with: go-version: 1.16 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.go @@ -127,7 +127,7 @@ jobs: needs: tests steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.go @@ -181,7 +181,7 @@ jobs: - uses: actions/setup-go@v2.1.3 with: go-version: 1.16 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 with: PATTERNS: | **/**.go @@ -205,7 +205,7 @@ jobs: timeout-minutes: 10 steps: - uses: actions/checkout@v2 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | @@ -225,7 +225,7 @@ jobs: - uses: actions/setup-go@v2.1.3 with: go-version: 1.16 - - uses: technote-space/get-diff-action@v4 + - uses: technote-space/get-diff-action@v5 id: git_diff with: PATTERNS: | From bdf5aee7a3d0ddd3c43960e443308dec92d33ee5 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Wed, 4 Aug 2021 12:15:12 +0200 Subject: [PATCH 11/31] style(capability)!: update go doc comments and remove BeginBlocker (#9845) ## Description + Backported go doc comment updates from https://github.com/cosmos/cosmos-sdk/pull/9835 + Removed BeginBlocker function which only wraps the `InitMemStore` -> https://github.com/cosmos/cosmos-sdk/pull/9835/files#r681987005 -- this is not a breaking change because RC3 was not yet released. + Updated changelog --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 4 ++-- x/capability/abci.go | 21 --------------------- x/capability/keeper/keeper.go | 11 ++++++----- x/capability/module.go | 8 +++++++- 4 files changed, 15 insertions(+), 29 deletions(-) delete mode 100644 x/capability/abci.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 032a3b5a85a5..2880158a22f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (client/keys) [\#9407](https://github.com/cosmos/cosmos-sdk/pull/9601) Added `keys rename` CLI command and `Keyring.Rename` interface method to rename a key in the keyring. * (x/slashing) [\#9458](https://github.com/cosmos/cosmos-sdk/pull/9458) Coins burned from slashing is now returned from Slash function and included in Slash event. * [\#9246](https://github.com/cosmos/cosmos-sdk/pull/9246) The `New` method for the network package now returns an error. -* [\#9519](https://github.com/cosmos/cosmos-sdk/pull/9519) `DeleteDeposits` renamed to `DeleteAndBurnDeposits`, `RefundDeposits` renamed to `RefundAndDeleteDeposits` +* [\#9519](https://github.com/cosmos/cosmos-sdk/pull/9519) `DeleteDeposits` renamed to `DeleteAndBurnDeposits`, `RefundDeposits` renamed to `RefundAndDeleteDeposits` * (codec) [\#9521](https://github.com/cosmos/cosmos-sdk/pull/9521) Removed deprecated `clientCtx.JSONCodec` from `client.Context`. * (codec) [\#9521](https://github.com/cosmos/cosmos-sdk/pull/9521) Rename `EncodingConfig.Marshaler` to `Codec`. * [\#9418](https://github.com/cosmos/cosmos-sdk/pull/9418) `sdk.Msg`'s `GetSigners()` method updated to return `[]string`. @@ -63,7 +63,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9432](https://github.com/cosmos/cosmos-sdk/pull/9432) `ConsensusParamsKeyTable` moved from `params/keeper` to `params/types` * [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled * [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules. -* (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. +* (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. App must add x/capability to begin blockers which will assure that the x/capability keeper is properly initialized. The x/capability begin blocker must be run before any other module which uses x/capability. ### Client Breaking Changes diff --git a/x/capability/abci.go b/x/capability/abci.go deleted file mode 100644 index 9d1c94b5b901..000000000000 --- a/x/capability/abci.go +++ /dev/null @@ -1,21 +0,0 @@ -package capability - -import ( - "time" - - "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/capability/keeper" - "github.com/cosmos/cosmos-sdk/x/capability/types" -) - -// BeginBlocker will call InitMemStore to initialize the memory stores in the case -// that this is the first time the node is executing a block since restarting (wiping memory). -// In this case, the BeginBlocker method will reinitialize the memory stores locally, so that subsequent -// capability transactions will pass. -// Otherwise BeginBlocker performs a no-op. -func BeginBlocker(ctx sdk.Context, k keeper.Keeper) { - defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) - - k.InitMemStore(ctx) -} diff --git a/x/capability/keeper/keeper.go b/x/capability/keeper/keeper.go index 48e0bdfe3184..61df229af9f2 100644 --- a/x/capability/keeper/keeper.go +++ b/x/capability/keeper/keeper.go @@ -99,10 +99,11 @@ func (k *Keeper) Seal() { k.sealed = true } -// InitMemStore will initialize the memory store if the initialized flag is not set. -// InitMemStore logic should be run in first `BeginBlock` or `InitChain` (whichever is run on app start) -// so that the memory store is properly initialized before any transactions are run. -// InitMemStore also asserts the memory store type is correct, and will panic if it does not have store type of `StoreTypeMemory`. +// InitMemStore will assure that the module store is a memory store (it will panic if it's not) +// and willl initialize it. The function is safe to be called multiple times. +// InitMemStore must be called every time the app starts before the keeper is used (so +// `BeginBlock` or `InitChain` - whichever is first). We need access to the store so we +// can't initialize it in a constructor. func (k *Keeper) InitMemStore(ctx sdk.Context) { memStore := ctx.KVStore(k.memKey) memStoreType := memStore.GetStoreType() @@ -137,7 +138,7 @@ func (k *Keeper) InitMemStore(ctx sdk.Context) { } } -// IsInitialized returns true if the initialized flag is set, and false otherwise +// IsInitialized returns true if the keeper is properly initialized, and false otherwise. func (k *Keeper) IsInitialized(ctx sdk.Context) bool { memStore := ctx.KVStore(k.memKey) return memStore.Get(types.KeyMemInitialized) != nil diff --git a/x/capability/module.go b/x/capability/module.go index 5d6e8ffcbb03..f8295948d9b7 100644 --- a/x/capability/module.go +++ b/x/capability/module.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "math/rand" + "time" "github.com/gorilla/mux" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -14,6 +15,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" @@ -143,8 +145,12 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw func (AppModule) ConsensusVersion() uint64 { return 1 } // BeginBlock executes all ABCI BeginBlock logic respective to the capability module. +// BeginBlocker calls InitMemStore to assert that the memory store is initialized. +// It's safe to run multiple times. func (am AppModule) BeginBlock(ctx sdk.Context, _ abci.RequestBeginBlock) { - BeginBlocker(ctx, am.keeper) + defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) + + am.keeper.InitMemStore(ctx) } // EndBlock executes all ABCI EndBlock logic respective to the capability module. It From 66b91ee7c7c9f916282d6cd1fffdfebf4cdf74ab Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 4 Aug 2021 14:54:01 +0200 Subject: [PATCH 12/31] added valid sorted coin testcase --- x/bank/types/balance_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/x/bank/types/balance_test.go b/x/bank/types/balance_test.go index fd6b72a3a1de..10ee2a74bf6e 100644 --- a/x/bank/types/balance_test.go +++ b/x/bank/types/balance_test.go @@ -87,6 +87,18 @@ func TestBalanceValidate(t *testing.T) { }, true, }, + { + "valid sorted coins", + bank.Balance{ + Address: "cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t", + Coins: sdk.Coins{ + sdk.NewInt64Coin("atom", 2), + sdk.NewInt64Coin("batom", 12), + sdk.NewInt64Coin("zatom", 2), + }, + }, + false, + }, } for _, tc := range testCases { From 59b788a3bb4ecf7fc95c062551898e4db505765b Mon Sep 17 00:00:00 2001 From: Spoorthi <9302666+spoo-bar@users.noreply.github.com> Date: Wed, 4 Aug 2021 14:56:02 +0200 Subject: [PATCH 13/31] updating changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8861b7145cab..f9937a297ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,7 +93,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. * [\#9762](https://github.com/cosmos/cosmos-sdk/pull/9762) The init command uses the chain-id from the client config if --chain-id is not provided * [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. -* [\#9829](https://github.com/cosmos/cosmos-sdk/pull/9829) Fixed Coin denom sorting not being checked during `Balance.Validate` check +* [\#9829](https://github.com/cosmos/cosmos-sdk/pull/9829) Fixed Coin denom sorting not being checked during `Balance.Validate` check. Refactored the Validation logic to use `Coins.Validate` for `Balance.Coins` ### State Machine Breaking From eb79dd022f9d5f1eda15d6942dbde7fbdd5ee07b Mon Sep 17 00:00:00 2001 From: MD Aleem <72057206+aleem1314@users.noreply.github.com> Date: Thu, 5 Aug 2021 22:30:28 +0530 Subject: [PATCH 14/31] feat!: change Coin storage model (#9832) ## Description Closes: #9361 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 2 ++ contrib/rosetta/configuration/bootstrap.json | 2 +- contrib/rosetta/node/data.tar.gz | Bin 38887 -> 35581 bytes types/query/filtered_pagination_test.go | 22 +++++++------- types/query/pagination_test.go | 6 ++-- x/bank/keeper/grpc_query.go | 11 ++++--- x/bank/keeper/send.go | 14 ++++++--- x/bank/keeper/view.go | 29 +++++++++++-------- x/bank/migrations/v044/store.go | 15 ++++++++++ x/bank/migrations/v044/store_test.go | 9 ++++++ x/bank/types/key.go | 17 +++++------ x/bank/types/key_test.go | 3 +- 12 files changed, 83 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2880158a22f3..bf41a596bc69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled * [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules. * (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. App must add x/capability to begin blockers which will assure that the x/capability keeper is properly initialized. The x/capability begin blocker must be run before any other module which uses x/capability. +* (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) `AddressFromBalancesStore` renamed to `AddressAndDenomFromBalancesStore`. ### Client Breaking Changes @@ -101,6 +102,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [\#9611](https://github.com/cosmos/cosmos-sdk/pull/9611) Introduce a new index to act as a reverse index between a denomination and address allowing to query for token holders of a specific denomination. `DenomOwners` is updated to use the new reverse index. +* (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) Account balance is stored as `sdk.Int` rather than `sdk.Coin`. ## [v0.43.0-rc0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.43.0-rc0) - 2021-06-25 diff --git a/contrib/rosetta/configuration/bootstrap.json b/contrib/rosetta/configuration/bootstrap.json index e014a1043261..6fbfac1a509a 100644 --- a/contrib/rosetta/configuration/bootstrap.json +++ b/contrib/rosetta/configuration/bootstrap.json @@ -1,7 +1,7 @@ [ { "account_identifier": { - "address":"cosmos1kezmr2chzy7w00nhh7qxhpqphdwum3j0mgdaw0" + "address":"cosmos1y3awd3vl7g29q44uvz0yrevcduf2exvkwxk3uq" }, "currency":{ "symbol":"stake", diff --git a/contrib/rosetta/node/data.tar.gz b/contrib/rosetta/node/data.tar.gz index 81ad29831d2ba70a0d4e5a11a8ac615cb2e2fc2e..b3b890e1153840cf0c97bc1f79158916062c0595 100644 GIT binary patch literal 35581 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

d5&;nl{mDeeqvIk}!76vE26wQRLpZMsfn&pQYS zP-K;rF~)})6Z|MzKYMAj&{}CPy$p)N*`|exaKks-@e5b#S+NfZ@1wOudy5Zl=P1Vk zjp0EdkxHGXYS+UE0yG3m648d%LR*UxNY&A%9lFFq>#yxlUKn{20JTv;Yy-uN_7?Ju z4}DAgtT}V@D%;G8qjyF|s#CQ!bSp$tp1Z(FfDQs-s(@w0&BJF^@BpZTN-)J++1}{3 zMNkjVU?2C|+gj3OpZx4|uTCTfE}zf=xDq$ODhMqkXgT+Yjg{UMdbg3hGQ2g=Ma41L z;vCk-n?b6Z@M@!^d8t*JNopCOK~_KjKe|0u?mHejF2{?i*U-gLMQWr zWB}Ad9avx8B(LUDll@5CBVPxHN%2YIj;UQA%an3T5KszB>`B6z}@Jq2BvESmv?$|0c+4j0<;NfNM-*knrA_u}U ze31Rx4|@PKK&5Eb-(?g%=o^yGEz9jXH1c}n6t|>&hm0_V`H6*_SF+XGpDBhYw}*i6 zGb02b|DA*xX+BTMkE6mlY={c*&zm5fs4{2iccvWbi#)p?ckrX zc2apG`?mYxC-t^ak>GA5=s9Wh=J8^3aH`?$8QBAX#;5~B4eR6+Dn0_S22ZCOio{9d z2R^OObX+q@U3avqzi-iqIg#o#O-> zIP(^W4q-(Sgk`Z@&k~m70BDBFE0fLr+Tx_offhIMIsF5@7iNlwazAQRzvg!3NDd7W z!8^j6a+Gv~ux1ko%V_i1+R9P@G)HB3B@=htC-hE*^*z_P%sEtN_5&2UPwnYJg(nhR$U%NT+q=$oL!ON-!|7s3mK*8G?SiyrqIIdBJ5tdpLgJ%!Cp`C9qC)59p0#;>Nht4TPtPI` zqEzE2@5qGK(d^2#5m76@i5kC}oqHHULs)V{ScYx(71)vjwFh*5!+;iiALBcnwj2U785sxE=B4x_y_E}|r_2#yBO z8hu61>Fs)HXF@SKQvM>kUM6MitL{JU?%dIoQ%afv%dQTo!pLiXs~ZuO=Fa`49|gPuCq9Xv-d za1MkOa}bvO(?JOXi&P-?QUGulD!63=yLj%vvYeBF&1(eO{m3G`A6|#bTHH}B zT`ia)>q^{=l;ppGBc~PzkxA4rDn68kx}cW@&M2Et0It#PQBjB9X{oMR&MmJbl&@xT zY^!T<fjZ^c;j0pb(b*E1kYSgn7E4XMJ`|?s3)VlGB+*{pI>OENS|M z=UVbM4jnn%^|ZfyM934aO)#?_!kX0}EIB%X?~ZT{<%-HvD&g7Vp^t;#pXD~@<;Kft zO7-l~s1#=_khUBruoSA~I4*|JsuT#T{(`VXFciAPQVoE3RCe{dI;FNHAFxxmQW@~z z325!i96I|_?vX}CNugu?-yRvXclsPi98&SVzG3v+e8X}6L6m?XBw1vo6%_uQHsRc% zdIb;yy(qpOv`ZG81dm)2OrTO+e!q^vP!U-HN6*4lgBz*|kNX}zd#jzH?(uww${S5L zl5j$?r3g`8cI3iQu9(H0Oi0zhiC7R!LB2Jk-?ly+NB`@;1 zASb6NFN=Hn{YM1>012oF_e|c69hTUcY8=+){;V&%bC2uBw`m2c!8UtJrsdouWyO)G zp&{r|@V+}}U!EXqozjAaRk)rQW{G3~^hCu9Ufi@}MHH~&#(+7Kn%MKp9d@gmy5DW- zGw^TYWy@;!qdhJ80}H~G0Ly~*L3??)9wMUBTC~hTAkJok8_vtn)Iu$3XTJaIdu2Hv z$#*Z5==E7@YS8MTg(5>(K?7mg%b}f9LIglBRAyN{9s+}A*<%Y+}(uT#~R%w38 z8Z5ipwYlI^?OJC^WI?1cGnngYVoWbZ7E~g#IFELoRv3+hiYBr}g2`1h>shB1ll5N? z0kX|8seYz>YUP$b%T)t))H4{Nk;t^dXJIhh(vKX6mrZX}H1?&?BI31ySBBRWCa;^b zw{rPLn-_GFZcRczhHbXI*)5q7 zngoD;sAId2N`2>L>-a&jdb;;k>{K%E_R=dR6ZN9fk;m;DH#|zQf|dSbYzRvP2#fH2 zZfxYi{$@=kglsUs$hbl9K6+IA^ z(WL6W3OGI~sQg~OaQ!+}m%rW_SJe<%yxx7eX5lFnz84=vf_4Vz{y4wh7OqMb<{pGK znLt=}i(L}y@BzTxsKkuP-Y)Bo-m&=mNta?D$Hb2`4(3`#efde(Bkw)&(JgJVaHy=A z``B1Un-W5%lKcpUc#^O7>?4pBIHlhf@YgZf{s8EYiU_8)tGjf)U+VZ^{~PC+HUev_ z(DM_gx@sA9hVJf{DN2nUfRiAW}R!1JUniR?iM;bmMngpIWdW&&UUDy~ej zqL;?q@1^ybo*jXlHfJ(KUf8`I?T+#C@%j4w)=4Wo9Jv363t>qGVOi#N&z{#600U7e ziXK``K72^ymUPmo`mxK(`<}nQ9dxt((52K+_H*0MNjr-ECJmh*eHEcoWI?thI9|Px zf8dQV6&1$g6z6`!vZ7_x2P$G&Qr3;BK59$fUOQmj@^+}&%L~R?DpyyX%rs;jyMH^|kO!=a-W5 zGeOq&p0FD*!67WAAS@eiBnD)`;S!9>({jzO2EmA?jh&aIi<~YNNJ38(w6r$!+9Oz=KEc)AQj9p0dOUuUw5o2}xt*F|?Djy`Q^ z$sbtRGKf!FvgA_gniX-h<$S^L5SAbi7HuaN4lQQ@+=EK&3oiOb&E%Hd2|XJ%I<(@C z&YUOgZwYd7IJvs?o=kemUfP@1Ib!N;ZXRCEh)Y^tD_0Fs9!lqMnGmGAjo-dO@h&Y+KP$na{9)4 zM10_R(kgvoQ&Cfgt*R%kwo^!|ChSFnKkj~|Cl^JBr-*fLStWl zk_A6q+?fi&+c32`cpj=A&=hJ8d*aM%Mt&W>;^LBxT5p;fDY{wmCgI)BShcwvwILus zARtQyPk(4Z!9`$#o7FZ`zevuy*~YgqxiwQjV(o=(xib>$$Wl^e+)@1ct5~(396gPI z!Uq8vewR?6mJY#@nBW-CNK+DzC-NeT!|{~#$JdjjKRESkb{Z+%6t{dkpK#fkz&}C% zLO|mN0y5scSJN#Ug12J=yz*7=(OR#iPjcsm-gd5-ofs-yH$%a!C{?p8WZ2!!f!~c+ z7&PW0ps57`d0|ljclRL(-hqj7ZGx4;aB&m=PB_V;TJ3X*q#sUU4zD$t1R< zyAovpSsen(SP_sXu2_0>q6_#aOpq!CYKcCqT;rg5%+<0aC}))^Z>@J?*`Jl^?p zR;p#F5t`G5iKtn=DmD0_=r}npaV_=k1=qh4nN_hQJP}7xtQJ;zx&wk^v4zb!+f#a` zRf$kv6+LfFqLbqdqbn+3Tvvqoe$cda(*95#}D&qcSL z{S+J1qOMT9UyEn58izTglpc|f6` zRXxdBBLWHq1Z0kYn3oL-Z#Oo)>;>yn56ivJrE-T0P7ym+IG2H^x?Q(!y=!iH?qjN! z1gmP7bFd+xK7)X4|MbXNnE}BG*zkHIb~gR})--OaNw0G&PG;xsh^u!^m5i-!y3akQ zZ+mE&Dl^F+I}k|}z8^hZeT~5L<3)PeF0~RK`(9eF^Wup8`eECpgAlw2TeFFMK%$03 zeW806!-(tpyxxYbg#Qs)SmYAUD4akr; z@5RA;v8_(~8usnO+Vf}Yb)!q}POH5(%Pu)=-M}m}qmszRiMKWM@7XqV$7fc@1ajZ0czdt(h=M&3$7C+%1hoj@ ze4*(7@liW9eHBj>1ntJoFRLtD3Ue!NJlg^5XLZZV}PK^&hG#jfG4} z-TrvOKN=Mf&)^dUHj9i|97%pa*I2=Cwxt8`488zHk*qlH9DB^E}M4JF2mDM&{kn6rTV;B^9cY z|2kzCI^El!7D#jVcMlFwaZ~YI@3z^~LwSq8nx8ir(kGQ?XFzbu1h7udyc9>?m7KDk zU+R{ZlrOLUOEJSw(EZRQdY+d~Ka|sQV#4C$2q=R)Cmw$_9q);$n#$$ zhaPE}CY#{U>396p&ei=+Mi!g-xwI>LS?LOAgb2v#2*`ZlOFHDG5S+%2PxL2zKY|1V z^!f1tO<4#&FizGO4H(;@K1;K$J(duBkiD%uvn{K-)VJ_@>tNX$1Iw`L-ZQmY&PP`^ zy4+lGaHg4Ng+`-JVPQ6NM~VQkHunU+52cFAbk%_)X^Y(If2{4g!k+uCM2N=q{W+}t{|R&raR1Atwg^aS+z6c>0U)4Nx2bcaN@6E0sKCRdCbY~aDw103_TI8 zrv=Ky10Gv>COaQm$&h%^lirWd4*g@dWAA;I-s`#i+Ib8{kehP_4Mb~}0d`?R~+wgsfru_N9A|;pJpEPiC1Pu9b=jxyuj$mjg zty`~ZO?&5ky0Q`>Fn0CibQGU_c;D75>_YjYJTsvKKWX3;3ow*2<=9{7oRW*7;hKH5 zX-4lx3U|t9_rzOPv7*n?+M$fyneWfY&3PPN3(b`ciEOE@2loZA{n?OdzQR(3_q%9DHo_0VW|b%*9Zk zWO4JtBn2YRD2W$I2K^Pt-$j~FIDvO@&B{h=h^d=8@+?8_i6&jjjv4nBt z2~e)cd}1suoTMuj5b?~;Ino-`FPZYky5QvHGrPk`Yfw)hk%)pm?mo_J?;m0Y4;s58 z0zhF4^D-1RxG>)mju(an847*@n|$3p-Mu_K0~wH*A&$-s!^hc!nr)~c9Z|uTMDq2~ zLRJj#T;}X}?;~aQ?kV}XO%273(pp9&bg00y4;3^Dg-%tY)2S5N_o0GDRa2!XQ<&Gw z&{P#wX*7zeB28HhR{ySsy}F3?FUToCuW=Z3a0tklm7i!a=r~b6;W&Woo> zPqB;P$nu8+juKDXc7>Jmz^6wanD%`bWHHE;+PBcIUD&|qELCbSUB>;J*{1e42{qFz zmVZX3ErGej405k&R$4pQEvEY8CQjlQMC&wPXa>1C)+*lhPT#ig+2Q8+uBM}@)|V?!#ECyHvq3Z6_w^?S0uhfv=?1a@ zNa@A~BlJKRmzPk__F?_^^tBmPtG5wq)Ld8HF**3s>xAT^R6hwcUN|#c@v=#IKO??E z`u;kDCxdo1L*BvgLFumb5vvtfs$6|Bga2_yo*nvXe~-=zgpUnGV^VuzRoAYMLk#7& z?@aoSFW)aBU>$aoE2w8G@8<`4whnk+{O@vCfed7W;hgk3r8e7HcZp$Xdib-|A@ds# zuh5b>%KcdQWcCFpA<%)BgvTJN0>uOZG7+Ya!5BA307&0rd%fF~c512ySH00k9a% zaKK!iYGb*D48cw8(l{`f;UJZJYfeog!p)<9VDgi5VylVHxtTHBF6#Nls&f=iq06s* z#icFcRW)wZ6}NZCFS^k>hn3YK#F<2H*4mgPqNYSuV?Qgz`r#xx&+xp4smlyJw_4^t zt-iGcYPhm7@7;cWmpq}G>+K6^l1$N9bY`3c&bi`4KT&^@l)l;6X8!FXD+ga?b9v4- zclSTBplwy-%~nHnB!*!GWD)`b#!0t!YL{NmuvzNauewr4O}JmyAF8+6+oHO@+sjy~ z*oNMxi5i1JdqpN9W6l#}Uf8Ed8TSE=lST=_+0#1N#&8M_}^gX#$Rg-WGT6n_nLAsi>2tG*i7R(&~qv$`yRzU0csPM(fwhZxyz4HDEU+eG2O zQyNU2oT;;HYSCZ4}d z%muQR6CgXoaNM-hZ*~XdlWVgS!j@&fwpqFTOL35NaKILiuXT_cK@bmO;R3}r0tR#DxeXOPqko4~KvTz75>b$$Aj{ zfDIML(qJ5L$JY95Nv#zD?jC)BdAr)LHLmA$EWRE1L^D%S%#YNUEj}7+cdtA**Mhci z4srFq5+zm&^B=9E0hI>*0kr4 zqcbDmF){SKztn#Fj3AzZ))RA& z4(CcRV#L(1HdNM)Y|@$q_%pmJ7F2EulV9C*yVWL>Zt2~1D~Fa(SE0qlC?_i+!k6~# zxx2%ZKKQ4|LE(rUzEn2>6WyVsN0qGxDz`np(W#Djv`EBczq3#NSa!|av0%;iV1`W+ zHOf7MxbAE3rA(T`|5PaXuzqF-$(O{bHE$2kqkJv;M!D z?!T*=?w3Gr^0#_FU?5v{QmJDTFQF_Y#uQR?D{^{K$vAY^e6Y&@3L&XJ@|zM0B|kgz*36S~rk!uqHdEapOGb0~YWL z7eGF!6c1Rd2ape{NCcKFaxVPCA)uF+pLI$`0tY`CeSmot>#y}|HapWgJS6`x>$1zP zPs|wZTJ}0P{aj_mpZQVB3HbJqejDbBi*wV8*>ZV3{7M0Z0Mpb1U$BEJo2E z4gvW!f9iojSu~Mi&tW(h{o$ZzBFCqs6L15ge_&pF$6gi!n@ZXnigN{TGq*P=h>mrWYGkcyJmPyOR8MVg!K7yfHNLwom)q4~or z(VTjtpjCf@#g~7#ckb~}rF{UOnZuBKV%rd>aY?zXnPJ=ty>dwxDwSvfehPv03yCfx zDl3(uMQ%~)B8pIQi?nW~mI`@OSz47_DTFB1hNQOMqpWw#jPo0I|9C&|$N8s!e&=_2 zp65LC`#rz!BXvv(NciauVScQNC(YAT7hqQLxtX22^vNbZLrlcW$eQZ)cJY@v9$K?C zRw{l|%&o8VRq^1p(GkF!g5|T6>Y}UWFWnIE6NL6?6rXGTE#8Tbj5};7G45}&JeQb3|q3k6JHrm5D7Ps|C9Pt?&*FI6xAvZNh2Zmi}PVbMTsZa8uxU8_5 z+B#$5>aq_4&e%}ns)PA6$YWM_5F>A^sT)2gnywFcwl=Fy=g1TO4{g*Kb5WIn#hD@6 z(j%hdHvc1|oAgRn;YkO@J>lbl4GxUb&rYuWe&~hlig>H;^6SftN=8J-wfhG0e~`zl z?)aXQSC;C@P!S`l+txNTGIz9F{*oie9X;VdZ0hfnrFp9Ts={0(6F>JI0@1wak8 zE@~<~h46uN5uObYO@svSl!Hh(B!H(J;)g-N7*9DwPlI?ho^pto1(8}j?f=s7am&FTnqA7 zZJGfaTRvDM*PBi8oYwc}6(9dycVhQ4@)8~MwwEVh1v&X2F33ro1^Il3mXnD_9?_ng z)p9#h>JMstve|t5s$oxw*1dag9{4L^1vz=tf*gooyahSwuYbXc9}<8JK+6tXm!lNq z2nvnsCVoE~Y=@NK*daLza)b~mPf$1JCr%!c6(x);$QM?2r_AR&awthBOr+5BI5oJx z*Dvx&&Wo})=ZX2Tr6pKFPG&F28B|?;DyxzN3Vn5l)&rT-ttNMB&h#DsV{=Nz^Lp*i z;rk2Tiq{%;W0fTtx3Yx17*l0Apm9c`cyIGjm%t9u{Q7Lmf(24`ayA-4pByTGpZuUe z9;+$`bOK z)tet>X`2kA-OVVG?#E=hmkSXL1iL!(^HWtSr##iNXVhVpB^kG}gr*CH;$`)w(d~9< zw@GaNOa2No@?D8-0Uj^u!J8M#=q5OZB*|Od#mWIPemMZeGoEr__#*{t4M+e_Ias1Y z0(i>7(-ueoPdRjU03R;kD2I+o;Byol<FQ1=gID{av_wy)uOX`?nCaO3@n+Q=wT>nOYp zY3XU}>FZL5FLp3=soD&!!Mhy{Z7PjMr|D3sbX{#O7dwW>4a$GP1ewdl4g`gQS(j&; zYC<4VSjUayVg~}|)_f5=MduTUimYn5UhF`?tebqU<}>Dz5{E~um?;A0$$HJLP-6fUJC#<>B8^3r3fteh-K=<)-W&gKH^Q&WeUr{6K zEwEo#A`yjRLLtYv+0Hp=KebWz>~CvZdSdLquQ4b*a-c!Qd6T+QRD8>)#6@y*2lHr{ zGi7M$=;%;+!gK?!1)ph3)V&rEL;U!|d-Jv0=?+V_yRY-8FqafvadO?+ZCJEp;!L3s z#>JV+@B6nGtv2)4!p5ZR9G|1h8cw=AYyQ(mAfeFjm*|_O->u;j9Bo`sXg*PB2)H?< z^kte7bN!RvRjW#F8%^JJpqrljsNG3Z!D5}jW5vRDXXC+%f;m$iZEbyh27`T}bLUJ; z`!QzG~-S(l`oI#r~XUctXa5LCaf3Z1M7Iiqe z(m%HA_`bQhA@A#Ke2a2Aqo_anTQeVjt*1ueOrZfnp=h`{MDl1|zGL^Pq&IaM2DdNC zNG}hQPL9=VNnd|V-ahkrm)aUEu^%^Q3PsEHf6cXckYTXYwoldj+3cfXDF(9w__vx; z-I{Xb-d(nz+XGLF#6{%}Vw_}>4V??diBLoY?|&uhGK+tXM+Py@frdE-Nn8C> z?n>*X$2ZwZ?YR1byHCV}N>NYi)a6l&Uw^|mUotIk^aSqP>qA$PI#_n1=Ca9AU)PvG zpQP|x7mU?bG||vE*=c$EBUG((*J?M3N0aQ*mLX zS-0-xS(n~j8|#XiY4u@_4q2C?1_ri8#mzo(V}UbTsLm%dKUt%!RqqC7q5m}se-Y+BTYExGqC zv3v_##~`JNLSF=%-P6wu&(Un3FI{yu{H&kT`vOYUyRLgr8G8TPUKtk?W=6Ec(khfqQo#lbal!9o{7N%G&uN1@=jI%G_}I!qcm z$Ybl(@u+{Ojf_b{2VVGXWMr&uWMnLDWMmv|WMph@WM0OLvyF_s1?4ZeZDeGuZDi~P hXxXy1U=fZM?8GziOgt0M#Pk35{0G~JOrQW#0stcErG)?h literal 38887 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≈?wVO zIUB=QF~aPp)~L>EYI>zN-gAroy@xp)$B;pfxKDg*Z~n{@MunNQQj6r*$PSZ|;|x%t z-y<^gzQ*;+_S-3!?P0tsyFo_DWgjFQ!>iX%IE`ERV&~qjpk>Il)2`~poh_AZS6i!o za|UR%b#J&w^(v3O&GWlRAr)rHyW8qxIL2iAjY@DPJtHyMB$7|uHON|y&8LX2ymNQk zw3+R0mu^$Nk4Ii=nzQ@5g}qEdjvrz^cMgWMhK2GG7V3V^p%>hYxdGe#<$RG0l=L^}01FjmSSS~`DjBKHXqpf=%t#mdK=d!+7=Nxf>MdbG`edj)0&L`$d9z)x% zXMLK1m-IywNnrkg|J%9m+^KaiUR9QmtIfQ0`dN^%$Cw5$3D>+J!vx1iEvgGgE9e{z zJMmQOiJtTLzN^8<2(Rm|d0@#zKNzoSM2L;~=U!=y2(^h-@mZBQC55qGGAB3t?q_3z z&=q$d9gKJVXavL%9FB#$F)Y+rm-?w?Sn_EZgbe3GQge=Vio=WN=NYQkZZyh&f#*Fv zci_gBXW=qSuOuz*9tYg=7~v5E%OhxX2lI&P(>P!QSQ&0g6+jJMXf7*(gY!+@<(Em0usC(9<(wF zT$I#DUZ}e*tJ8Y9rKn|g@=giEGh2Xp_&cM*LU|Gk!mUymspVoN;kPCHuX%?*RIRD0h_wXP_z*LOg$h0_lvkpbA~oz_yb=f@W6YGB z=6ku`VNHhV6dm)CQQGNkDae=YwrbL|SUs$Ek3$;3l&eSx9b^A<2|^Cx&2W5f1>^m? zEyP%Uktv1o>1y$UD>d1Rv?c`HE!Qo%b51ET=Aq>jpWiosn$Q=*l0uCEMuONEXqJ56 zQiT{XZyercmh0IO$GN>E*Zx1|K_ktjYpo{mMhAL8sbQJGcUu5CH#1$4zcPj{PGw-6skz6?4)W_S{e5 zW_7P&z}uDs+q@nw>kRh_kJ06Dkc7tIi35+g6gr7cAyY9bfi+^xUC;Nu5hN4xJjeNP^ao9uNq{hqg^c3aMF#Z#G^ob>!gt!-4$IF+pw z7(LlYb%R>wWp&O%9wmGsOQXH7-tt`lgHIUx`cmspPhLrjqDkJ09F6W#G1x@OKz;E! zFcqmZOSeX=tOwoaq&~GQ9{2k3Je)TSt}^t+Vm;EM4xfs0I(FTzWBIs%cl6bTI#M|% z@?*l!S}iwMuH|qXgN3>$EYzS1_guxSgu_*b9!cDkt$~lFL!=kmZl68p^r`7V%a+Cm z&hbliYub!28~LU87^2T_YC`?i4M&qeiLnP`gY#y#3#7Q&mBMgiGB5_{34GdQHj6+e zGQ3+&<8@H4&NYSNID9Gs|{_j;fRCksM}$! z-GyslTajmP;7X_c-l-*fA@}Nw`U*qa6b=T2Tf{7G0mD-tdU)*athi$K z$myZJB;-f+c#Tg$^m0e>n#xje{V!t0tN(+&SJCd^9iZ)8AXuDgrXkWBSDI{BI z!F$F?-nTiO?)UTj_dNgjW1RE7zTe+={jTd==PZS*pe8cC_lMt{5@v}1?NG36^d{bK z#Hl=c==h_+d*SHrpR$7UgL9b;sMe}1*Rf~)qH%sKRN=`s6F zw?tsoLhg&Mi2V1(@_*Y)EC1nDyUEPWZ4CgMISZkM_l9qKx6}fl7BZE@y2~1aqEu_LL(Rb0^h%+M zMe9yxF)u%{Y@elcSINL)HQX`(ld1rrRX7Mue6P7?L@5AjBXdrZQ0+WeBqo%Qb*Cw7 z+F(J?U)GO2oS$dUX^{vLJ*W0|yo3IA5rxp^2?)(F_W3&TL;zecmFA48G=H?7XnYN# zM)tVE{qj$WIWg2&au@~2Ag@~ce1D$f7oGBj67!1|4aAFewZ%N&d!kG(Z)lZgP2Bi4 zwh(WJ{QFL%(E3xK=xzY0iwwGc&*JxYT{4|7Hys!>>N|Gy^BM8it;ABGf8RQzX_Cng z6nJ~j zMQ1p+Cr}|YSr8h<`e!67*m8Qvbn0uH4b_k5-LL-Eq3$r_V@agW-5%<&>>RGJX}C>} zrz+oayxqS^%k6dS$X@|~`pBRUDsiu>WEXlJ3{tIL$EevP^pRwT8S-!fZR$1u&8e0wsi zKGb75+*9#w2%&i&Ler-ex$Uz%04_pilO?9Y zPTM8}HAUF?;zFUZk*rA8q~SeY68*j5wsVD7vH7=1o)H41k8n;IUovtB?^nQ>Gf+=my*I2QY*t5JDqr-se{jGcrYHbcXv-S*zfISmE%BPnGg# zN2lq;A4%@sW1jWNQ|_kk`FCz`Zv3zzgcb-8n!@ysTFdnSNce?~E^RuKvV*%y`gqfl zGd0m?a$f9>b?xc1F$$Bs%wEt_;vyzDnI8~ZYC~xHe4C*@4*)d##WYiR_>cZUUrn}@ z%vf2Hs)oO3r4I4JGcCu!mxVJWSo$;IV2}`^QW$XD(Le2EdZMTB5KKZ*PKw&Jr26ew(zAofduf zO_6~CLnpb@M5BCWL)=o^b&TH2y{%HyTwt?(hlS9DL1-i^uF~(oQdQ`@uSYJEob^}*-8;x^$5hi(Utrui)T9s&F2uBHWvK@6|RTY$ZTd`efv>s zsGquHfrun@YGVdQG`h4siZ1RwU%pYrCNz&<4_P!aD+2BuC*!qfLHRy7w``Ds3rag% z6seeovBM6d87}#4aJr{#`UZ6m`b5v{|CndV&#j4$bP{*?n(>Ma0BDPhn`;}C*XsQ- z66+Y;P(0EWECk4;7~d<5v2X|qI$_#Oa)sSFi3_30gV1O$^;n_?3tfs#Xd}UFeR#|+ z$6_3zM1fGe66avszSZ;%u;6W`ODSe;GhgV$<&$xnE>tf9;}Vf^dv!gomz%FSHAF5S z+)%C-)J3)K*r63@c-ePBlV92wA3iRZza=nL4_l^WG5;!G!FQ37s?RY}RG?I4-wVNc&SH zdd$?c=~}l;_ApEHiC9u}MYSuO5R(Rk(EJaf>8n4i@(P|f9gqp7nZ+o|sz~PA_OAn; zrmgX@w#2iPip7;2%;)yRE{;idgQuwj`GN2Pa@kZuGo}*yv4BKl zHaZw#V-J8%Q=t~{px~Jb#|XZrkUT4-yM`RP{$8q4hSdTM_hLP5|B9g*W(C)`Z(z&t zo+}7X1~;fUJ>41rmrn(g{|@$p6B_CV7O{CA06I^FQR2aT@6M(i<+^jJ(Ww;xU4G(E z4h_XIOuYX!ZP?eFEAn70cEDAAZ46&67z zBybFk=;3%ugt9OO6T)PKQNl=UW;BJtBE>M7G!?NBPAr2Gt0F#88ONT?E4l}Ou7Y)0 zkb}k@@W?5Q}+>0xZnQMMA76xd3Ai?PQETb`D4WUC$aV`MyFp>YCNSk z&kFY}9sDg}fm4LB6R32SVSGdc%P@g!L@vU@)w93xo~~C_{;r#|q_vte*tnrR@kGaYa}xKO1;7PR zur>(I-w>MNMt6|7eE{e#m|59i-6yTXa72f)m{1T)Jj zTOyqwC7-8R7f*If_DR@^*RxTdKlAUNhqZdAh}^LNcy1LOP!L+0KxnGB;@x!{0dR$2 zW_qS~N^#y!epRF!XTL5v89a8-YE+e!t5E8^e2l1fnViP=dA_luov;x5+VZ}Ls8hlF zw7WfDR8fD+=QdM(9>p~=W*JgrSBIN$O`>RqVKGK*lTb6ZS!}eKS!fu6Kuh3+)0BlU z)2428_?*vDfCa1+EMO#nc~B}SxZljPa%a}Fv4t1=>aLU8A88$7z0$h0?P%?UfHvmL z6W!@TR5nQ@%~a`HTs7#;MnjMETC=ZPX`}^g25|0rZ;VHzb%{QTGf(vs^G$1 z=H{VnpY-|ss1?K?LUSI3W}LW0i_8-M=p&ffnl1e^GFsoMFI4-m*<&|tan6(KTD{xg zQ)t;IEwu+FCFy+A3OX$s@mr>~onl(k_?U1m8MY&tPBk+zNT9Jy!U;x+hS3xnjSy-~ zV^LyPv&qUJW;)y+kV2U>8YPs?jIH+hvIbsS`3hDLaS@b^q(2zsR^;jBy$rNpWpvLc zzgTCB({ahN;)L;1?P}9)r;05JJ-oZJx=1nFR=D_7}M4OrFiz z{I$}Bawj4ZMAYSPX72t;q`XMzp?`0ZeDPmfzYZagr+)kj*cYUum=9ru=1Gw zXjsuGu8v_c;D$Bmr-t?W=G~Nrb(N;o-2ebwHTBwsyleS895{xjWA%QlDnW^e6*pea zxxGV^q8cYAXHlmX(!9;Cq5T!aJD0)B9h?g}-i1PEE{%Zi428=HOB4%F{a7eP;`k~6 zBq5G)idGWXE5Bur*6Zw!^riDU_h(tk9>D@FNu0P;lV?TEn(zp-#z*jqqKBt|}>iRG7<} z{>2WE&za9VVE;2gukQ_(7?$@u7hCEd-w4;45X5nonXO8AptLjcWZrydue0Hxv+|9a zFMS=r^5&siy`=p~GvRS$$H!R_50B&=NsnX;V@{Rr_o0^Un!!x1b3TSQd^$(9Q zTHH1a9uwR*@OYpIv(VrvPMs-o(=#L@cg38^6ysSgk;3EWPKo7xPC^9sI+oM=MjOuI zaKr&VwsGvz>Q?E$a+9?Z4olj_X$_TnCws5ulJa|+SB`!YFNbH2zc)angp)ZmHt#!} zb_{X|jS?opj)gn_lhXGK0g#F~!t#p1~}*=5b3pCZJTLSe`40wlav&hJXI@>N~3k z@pHK^c|k8XPHze?p@1OHoaTLr02qk~3|-MM$Ugc8ShMWb=#^TX$j|HEX%+|0{>s+a zdZO#C@PHL>;tqk=RJ?Zp$6v^IAHP#82}>u(lR{t@vapM>1_pQoJl@O%yKwwxDw+4A z-Xd(uWUyLn?jz4#F<%PvxyYTY*UX7{2*LSx)%(wb5!$ZGx zh{G!e-yLcdy}Si0 zl)sqqOAMd3H<=b0#{2#apZDw}Yz!xa6irDWu@ho=0|#Iyj!R*KQi=kc?BUwOL>#)T zY>)TjL#mah+uMc1sCq5$qEfT(4V5&^Nl8;2oagdrqV@3+0^45+tb&1O10Xzr>{~B%-wjzypgyrg8*9w!+(g0Tgqgx*klIVo7ypO7E02e= zd;4=wLp7G7Le|@umd857kStLt(n6M~gki=K#x@i}MHCg1LKI1*go=1lwBeVEG>S@D zTBIb}7mt4TJ;U?J%IET41E_j_IEI_F&HzR&$VS}9-qmwtA4m)!1u zNN-_~NW}npR$b|rWAdn%0(h651_d!U(SsOLB#I`5@1z(h%ywFB4h{;!#D1Ft zsan;|jIArAQ#;>Mr?uFU6I{JDRyN$i5a~SlR|opDLV^YK9*wW}()!4W z{x~QE6Wn%>sA^uXNKR&Olfuj=ek`)o%g383HrEYGHTb`A!@nX-bqElB0)Wq7FP?Y@i?}sjVk>!f@3ABiGQa`R%r=uH2~hFX=Oy@idH^ zqhq4NAdkTytA;MMX)|!pCQO1p(k?@sr8T}kk1#Jea7x~*sdmVDWw%bhM`)4=csMof z557Lx{r!i zla#al!fi8+S5j;QN&QO{|-Ey^mZLoWCcBkfH+M4dtl{JQs@*I#J zPgH&ct&84`ol4K2Mx9tAIUoNrpzKoDAnTBG0yS11G^JD@4YGe zAm@SPCcse-gJv}t2DvZPL6jpvD5EmObdF@o;7gvA@z>w zh2m3Abtf4a+!q8>&^Tn%eSO1t)qEpJfg%1uA?RSCXzJ*Uoi>rN!}bbd27CGYGPp%_ z`OZ`$MM9Zuf0tj&31}*E?~T-aBx{JmG@+sAR&$40h6&^TBK@|7Zr+M}ifqZA+LRBc z_e;b~YR98l!&KCQP=EBrJFl8b1c~?OIAi#5<5LkNZI8x8K4v7nc|oFY=Y{7f*5xOC z)W0$0RQDWvA{=XX7mvn-h$r>}#3ypvfdX9V%5B4^iFJDXM7Ha1qS?>As7&FBZ|Y zPH!G%YzLcsap_COLEAC0LMJsY65oaszudbTZ#d`vxu=fuja|>@_bdx+m719S#E<*5 zWDFJzbOFeMcb~n#A^A`YCann~>u2s*Guw^iWocz&n6&&v;N#l!Io+(9)5YdJwhK+U z`4GR8VNlS(Aba)olVv?|P%I|1>}QObeb$G=CT=LRJjl|tEc!TO;k)d<^EI8cg4fkb z&g$rb$Yr*S<>AZS-mK)MBK+Szkj^(v=t0nSV4{)dtKnT|WjD8L;O(Q=2T_`0_W4;! z8#6FmRyePnr2=f}m>q7Ni|x34Z} z_r5y#<6&-XNwPF|+2limK`wzoR$Pm{)5^j@@tDLkD=O2zB$VU!ro=wGVC-==gCu!^ zdQ^12l}ytTS@xy-I*1!&ekEo@XEPUj(OI5M)EmQJypHxp9D~iY$3Z)>*J|DCrOEaU zOAZs)>i;`Q7Nt5OFRj;smxCn%_(uPH&FKl2;|+pk0`4 zyMNO7A~o^pU;cLYwp@)JOqN6Vw|kmW&3|H@Z!K(Z44u?K@D6BuOo zvjqwteQ?ljOkyip8(oz5U$;4Vw^O%g%J;ugrx2=jEwq2Q?tbAx-_B{JAfci#a`H;c z%zqP$?d`|3qO1;Fq z5I0me)sr_<^YsHsY)j;RBtK8V1h-CIUH9#Vby250Rb@_%OU4C;!ufrV6@RwBD=y0o zv0vwbbOR6^26+kw*;xD57Ken(K1`mr3Tx|SBOB+opVd6G;j~8TjsurYh~~>IC4XF# zq8?X$fcw^CoW6iT@dkrDA@<0pf{lYxF)8*ap1IqxHS2u;K2`ONnT3iMEM*Kobyl7m zQWPq-x%xvJePXDH745eMzZgcff6@*q3ILb782hOg0Xbn@3mH?j1B z8}1VC8Fx(DXP2P*2NEM+zoEI;FN;j7)8kplbeV?P{Iag7_GA|~2i-p(pQ(x5e~-gC z(L0^fM^BnO`RqOg@g3adoUeHp3NkQNnrrBNHNV^I=gF{|M!)M19-yC;NsfRZ%fxW&&^(hisVpQpO~-6` zd3e^XsWPj&wzdATzpXpfa{I@klV;}Mqk1=|v2r%-5OzfTDxhT;WHt<EIQ8&;Kl}LiMw-I%fEKzv$McDERJQu)=IvSNkw@e=RK{77 zMAfI#M(;mKpA}tm4&b0em}u+04qxgso?5r%mCK|Rq=6{A8z%k>-q+Sp7iFtjMzz1@ z9`q5227~+ngDgp&p;&{E%fck5)SzfqFxR%uo>jB|R+gBo+qLLCxs+;>fs^FJ#~EKRXx@N9#+w{fN ztuktW=Ux}8-KyzN^EsmNmp6W4C9T+YakaD6kNV;i&3tE+)&zmH{u8ncfhilrk=@*} zaTxxP&Fe_qu&zUrWnRQl&)x9xjWYSwU+{Vh7`7 z_xE+r!a)Um1A^~3Bp$cBI>iQ=Ur&MNS3J-PHGJZz&|d zmPn&^vc%!d%xq#ybDr@__f_iCMX#9MQv5lWj)c^>651dZKnd;9Y@{<+8RZx2spQ^D ziYxbQzLK*=ex^royVmZ1|S5ylmz2K5^ZvKKP=n+@Mc11esF3XIn{P;(trV7$gbQrtkT?(rH2 z36TSp%ExOQlrjNxDglz>co-CCttvP5B#6-E;R>kKtswc>9XXssDr;lk_*Y(}dQGh# zV96+3C(JWysy@}+o}k%uYc?`2AskFp92tKz^tE;L^~XN$6VxgfIQ&pzXN-Dt{ITrY@@Ugo`r?8bbbD7>GWell3OTxL6Ps>cBa;(jML+vz zfA-@8LJh$_N6nFe4nZ);Bp5_Y5O#HkjG4_FDFmD>C;GcfGbN_<<*63Kxd` zt94K{dv9qVV(ouPvKwRqSh8CHOImDl+|OCxjo0^hc9g7lhL7?g(ED2tKE58{{AuWQ`y~k1xB~4O%r zgRB)q=vLeGZhG9s*Xv$b5V{_0XB)W>9g}PA`np}NYOacw7{m}3CxB$JL9q>ktcDix z5#w7OKwD$r-uZVxfJ*qF0>1+Syu<-i;CDbk7eN4TiufH6;N=FO0>1+S6kP`u_#F_? z4F|w05Pk;)qz(_1kN+JIkX$}cPycs7fENpZ3j7WT=yC$!?FHjV>*Hb2#An?|xF-Zb z&BGPAPZLK;>q{QhBb?qg`57(TZZLkOi_@boRF&N#$Mb5hpQ=$_U?FM1%?JOH)<<_^ zI{NeH>riQbOY75g^yg7^spvK5w0V?yG#Ygtg{G^|%aOS-d_Dm^@&i5-6d=-%he5`0 zc9UvsgrSz<3g|^hW3*v+6o-+o{INkLr^F>xYH`K)y7fzm~8nk9#l{I_4Qll{4*+CnQe6D|7@~_FE5fxb? z-X?{g;_Oa{e)F}MpVRrM<%Z#k%KtmM)(H}EntFG)^+va6zE$g%E1Qr$|Bnt%KBcEn zHEk#m+WP;x*8f|&)?)&>=bE_%)6b7ajJ+sRp}bK&+(x{IPMnw(+$OhhWwSz)1v1i* zI0kA|!=Mo{23OWp-bT~BWLkady{CvRBo4@=&T2}~etYnHb_HZZ7oHfSb* zK?aP$O+MOXU3Y-9r~y(Jt?1&ffM?z7+Dz}&eQ!upj9T-g!eD1&y1yPncq-w4tU3)c z4l*9iiGN`SyjKCfcmcEz62%6pW&_#>X@djB!v#xl<6%(e*;k0Go8kV=-kAr)xPA|O z-kE6{i4@_Ra$hNHrr8&yXr-hQm9k7tq>EX-s@Gn0}xU=YD1)mP)VeW!^?rDUW*3esoB zDNu=02`pkO6IjGnC$O+jK)C?PT&g@3lFp?PTa`=2q@g1`oLnmQDJZ~`$faV^PzcmC zmx_G=8rn^Bsl@i?Qi-k3rOMMajk#1JoJ*C=TN0b)EsM*qq$N_3hYf>R{8exg_td2B zRvD|3wW5@SZc&eqJoa9x__w1zF$)Jo-5pAJLMu7uM<7zvks+PqHg4!iQy`XjY3m@q zk9DSIh`gG< zc?%Nz8`uwc<2+$4CwyyF*nY)3&jx?%a@;HJp!NZ;w3DdD3&Bvw1LI;|t`6RuyrG!c zkEZbc;(#NxLFZP)YVN7l``wwM)Iw9E&}cy+7bU9k(a3Gl>4Viz=bz5bAN|JkmAZD!E|Ojgz6#HrKC)+3dNwzP!}z z^7fCbBAu`{ZQI>lQ2it-VOLuhb2!=VvfGBBM2}ZqnsF-Qr)ro)Q!+A7-0pwKKg5|# zrL^^;F33TNYCIdw_PURn*^Gac>$6GW%X`JeS(zS$$6-4@lsmjEJ3)7*s!>|Fl?xgZ zDC8m{_+Aaa#yF)RJOM8~gal;hhVDxQFI1GF8@xsm5|E)Ay5tPJ1xc@iv#uU@}=u4uiqsv2o6} z8+!rTf`NN9oU?5b3OSSX-l!Ct78J==#Ua~Bgwlk_e&z&3QBtravW*1paIbDV^cibK zYJ)bF#xD3qM_FE}PBtO0TCtt8T=|55B4bJIyM^(q(jVu(+>IPtfBsx=tP7;cHgPvF zB$e#`r2U&lnbEplz@-y2<7fEB*@4?)w`w>y8-oAqpV zS;})q%`??~PNw$jF-3G^!PxXSN7e3ZJu9Srvmgf|*lfhO_}`7J^m%yx6UCeR+2z;N zyVVgC^^!1+(05e}!h;g48alRRL=f^Gg(@#un*>ek7l+L{MV>RPUGXR>R@Io)!>@m6 zrH&}%=)uC9vr~lF21zC&2#pLB@)rkJYh11%*b$~7WhjS^yudRtS;|3j5)zQ59He0( z0a?l+q_Zu|XJc#%;;oPX4u42Op+PLF-fp9&g!F275VXV}Y7HCMe~FE`m*N7)e8SRO zX8C&-navqqkdo;YH%D1>z?y;e&tyqTios-Y_;L|+zHWQ&XO+BW#a&Rn{?*HM(Go+W z2j0E6M5^@D&&&3?p{Xg>-5LP#P_OgGO0z;AEB|sVZzt!)y3VFoUqy`9yRfdNT5INA zot5U;GJ+r|24A1tS0OwmpnuS-N9cC0cx~i?dcop)$ z{b@k?lp%%29~cW31}hn%z5bl7vn?w|yWfhi8AK*S#$cm}DvB1suANh&%`sE2br#CwXq9H6>v9FFC&yi|V5!XjBIGSzg+3Cr9!*?#YiiO4x6QjOuWfqiZNoAg zYE6!{=yv0A=|Yx>(v<=Mh|t(ZA+KfNNR7`PD4zr-oa8YAk-RYKD~AyX$7~O;*=PiU zQDItD4kHkP3p2WM7ybJ_zK&g06z-Hi<T<0`$= zUL}i}HVV;(g~POww3VsP>M^5}>z1b1Gi`4_$Hj*on?K>v%a3Pb2GiI0t@G7bKjqOL zw&-G&b%i7HjcNN5z*h~9uFlZjHI#OozBWE`bHCd=)(lO`y7lMWAw7G}V4{|8R^|ID zXY86;xz?rXfu6H|ziHK3v1wCm2$S|H&cA0h9Q}2E@uyvrUK;N#_+@1LFJl+I_^l&VAEQSjNorxE++%n;Kg^rqiyMW z;n`X@gOs|@40WA<~n194& z=}0Klc|F$cnrA{lc#`MSxGWtBM3IK>Bn7DBmK*)5&8)lBXld!PEVXm0{9Q_Cb=RlczG$EFGy;EFC5d9Y^7? zbl9h$SOu3|YeLLnN}vJUl$ayI#GE9NMruZ+)lMIG9h(m$!1+K14LviqwL%n;kqxRk zHxI?AeT}l0THi}OR;6}o(p=97#XMDY-~7*yJg@v7FZ!Zii9Nxn{|+iUl&oj0_O2j6 zYtY^Dq%$Mt_v~8kFeSP~$GFsTTK8Fbo5dj@W|)l=0+O*1@Kya&`Ou12C+kDNc_n{L z4zfA>#bV-&xUiX5xr1k!Uz=u9GBW+)D-!kWzxI9$0p~sGKTxG6l@`6q_Hf3-#iDmN zBG_^c@2e zygqj8ry6o|nitdmtRs2ui;FuuLckvq0>jzSJdzmL>&iCyAjsxPD9$cM;DX1>c_RfNyDt2g=F%j33-YCY~*P?V{1 zg@BL)Qg#EX)N)rZ9&x|h9d(uQN$RVO+363$t~&3v@qK7Bwz|$$MD0OscL)eMhaW^+QoP9Jb5W5A8PK?Fep&Nk*Z;Q66#4N0)$@H=|XW5m018 z#f?8Tj0lK@q--;Giws7P+P6&xU~0xTQzOZ4k-^w9ci~?y#sp*>#Lkex_^zfhYpDhi zv24(UM|Oz}N?flQZy6kA-$mQ-OXVButWBR5=}}SF-~&`jWQOA;?3w(7$u3fJvWr!w zU-Zx*w|mJbVT^M0NTJ>J{JPUuo>W!pyx%slWXLfWY(;Ge>4H3HE7H|CgrPVc4CEmL zAe06Pw19MxQLtV8a%k8jv4N2;GU33wz?L2mpSNBwiRSW<-g#o<_pObs!a3;a;H`3PZ#%_M#+J@M~_Jwy7z=`?r$-*7y4Ij zJh5WXjgw66H|fdb7{^D>PjZ+_jdfGz$G%APqB^Yp$p&Z(rgS&TG%0U249|95C_`Jj zkMikT<`=iUSh>M>z|g)<_Q{rpg{ldMB3^6S8;GMK7|h|*d2Bkf+1EtkcFprnL2jj! z{STCWsY%HYI%^cZ*PYzYRLAbp{lir*IjRAex2*zmAQ##M=A<5;znj-}w+){YoS@{8 z{wZbfy}Mf|{|YcI?2~C1=K9nH+kv(O=0N?4LQQ~IAuiYF>{+`bDA~~~*7-V>=1B=C zt{PU%d$iql>vPIVT|;q4!C(%L$>nmH4C#T659a(bbF|T+MOjXsj(zhhx~x@8(Z4@e zHCfrDYl%}vCq2_&M~ZF2U=IG^z%#f%?BYA_)S;Z%%Mo@8be|R44ke~plRDq(zjdq! zJ1oSmK00mz%z+vRg}lM5kkw1Ix3pgi1}G{Yv^MOL>65#%ioa&7*A%ae`+;7W7k0W} zp-h{>9LQU|3VmHObzR!r{p%^Q8$WbET>NFC^`%X7sXebO-7s<4hT|$aB1%^(1m-|v z8-=`Gy3zisgVTl_^O};feOA$1#o_e<;oomhiT8Ruo`BC~@*Cft z642R9I+w-bG5CBYo6Y6WnM@uV?`E*ZHi0sYU@8*l?I|(}Ib&h#W~5C(!hpA@$OPSC zX*s8fNVrr=nH{G~TXS6oxizV8vK*;z$s{wm)t9zl zpMXxKq!Qoqcq^T2Cbufrj7dW~ESy|3_9f@b@OzXMw&oFml7e(|2|R{sq}G?A;m)dXY9cG?iHY+N>*&=8hO|E&L=!KQOL zEGC!7W)kQOE{jVip!6T&Ay9&`#Ul_!>l;Fr9Q6PHr3LK&@uRKBnwd_tAJ))OZ75*> zbGcIe&*N};9sU0^N+#Xj4116x8s6?9?%pC-+L*98?xC}T+>QNw#80L+2_X}9 zugarR#BZUKh~)otr8~+GD{`#==Iyls&3_J?C%yhNd5n(v|1*j~(}Io*_C=W-J(Njj zGSESj?2QMbQX)zW`fw(FID=2)aIg&v7xng=W#sKX%iULmn&o6z2m6@K_I5`n=46O} zPMjU?j#}qrh`&BE3^mTl7$X#Y`+ZFP@Acp3i(0zpWLUa~nFa>>1)?T7876^#{{HR_ zf>t>hR^r78wav+x=pKL>K<#rf#9!rU>MisSau-R~h|A|NQIV^WpTD~=D*gkDlqLV4 z$H)8%aSsf_hPM&c^>f5^cAhG(E`5ynrWV%JNAD+rO598A8`0XXiNWdxU7!;|0zXw|Nnr)A3~eXf9Py(NB{qf(sBOt zzg?j9zukvFgtnaja5$Wf^Z%bx+H?NHrSk=J-aqO5hs)s5S@f3Af5aCbGI^~z|KT#} zEFK#;|G|XVEJ3sL9}bPhXYhHf|7*`&cGCu;Fx+*X0(na*XJ$ONN?FXsra)=Pg$TTf z06SprHQ4l`zI(?+idvyAl1196{3{=8j{oEFd}l2Cb$$QYWNC3%3>Rr!HvhYFj*@q? z?RxcWj|2b5+&}Wq;FtZMlQ)0?o%a8i(f6}T#e4YA?Y*yf)O%Z=$kzEPb}<~H?Jn84 z06zrjWHsF7SyB#%X3*~8?w5GJ9oM@-6f|sHK-T@ROA=X4bs+l#-5H_%O4$1Db2q-{ zIuCskRu||t>Q2qPbv18B7Z>YUTaWvr-Lw z04AA~puOJaRcPGKv)Sdo z?`tBO%VBG}h_k%dFN^3(_FD1fU=r>1aIz-fK3Vl={_$(&Uk6rLm-x@|f6_@O{lEGN Ly-MhL0FnX#JW~%Z diff --git a/types/query/filtered_pagination_test.go b/types/query/filtered_pagination_test.go index 7f462a603947..01228571b59a 100644 --- a/types/query/filtered_pagination_test.go +++ b/types/query/filtered_pagination_test.go @@ -171,7 +171,7 @@ func (s *paginationTestSuite) TestReverseFilteredPaginations() { } func ExampleFilteredPaginate() { - app, ctx, appCodec := setupTest() + app, ctx, _ := setupTest() var balances sdk.Coins for i := 0; i < numBalances; i++ { @@ -200,16 +200,16 @@ func ExampleFilteredPaginate() { var balResult sdk.Coins pageRes, err := query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { - var bal sdk.Coin - err := appCodec.Unmarshal(value, &bal) + var amount sdk.Int + err := amount.Unmarshal(value) if err != nil { return false, err } - // filter balances with amount greater than 100 - if bal.Amount.Int64() > int64(100) { + // filter amount greater than 100 + if amount.Int64() > int64(100) { if accumulate { - balResult = append(balResult, bal) + balResult = append(balResult, sdk.NewCoin(string(key), amount)) } return true, nil @@ -232,16 +232,16 @@ func execFilterPaginate(store sdk.KVStore, pageReq *query.PageRequest, appCodec var balResult sdk.Coins res, err = query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) { - var bal sdk.Coin - err := appCodec.Unmarshal(value, &bal) + var amount sdk.Int + err := amount.Unmarshal(value) if err != nil { return false, err } - // filter balances with amount greater than 100 - if bal.Amount.Int64() > int64(100) { + // filter amount greater than 100 + if amount.Int64() > int64(100) { if accumulate { - balResult = append(balResult, bal) + balResult = append(balResult, sdk.NewCoin(string(key), amount)) } return true, nil diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index 98097642a1a4..efbb0fca16fa 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -319,12 +319,12 @@ func ExamplePaginate() { balancesStore := prefix.NewStore(authStore, types.BalancesPrefix) accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) pageRes, err := query.Paginate(accountStore, request.Pagination, func(key []byte, value []byte) error { - var tempRes sdk.Coin - err := app.AppCodec().Unmarshal(value, &tempRes) + var amount sdk.Int + err := amount.Unmarshal(value) if err != nil { return err } - balResult = append(balResult, tempRes) + balResult = append(balResult, sdk.NewCoin(string(key), amount)) return nil }) if err != nil { // should return no error diff --git a/x/bank/keeper/grpc_query.go b/x/bank/keeper/grpc_query.go index 6f53b5abba1b..70c780dcf5e6 100644 --- a/x/bank/keeper/grpc_query.go +++ b/x/bank/keeper/grpc_query.go @@ -59,13 +59,12 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances balances := sdk.NewCoins() accountStore := k.getAccountStore(sdkCtx, addr) - pageRes, err := query.Paginate(accountStore, req.Pagination, func(_, value []byte) error { - var result sdk.Coin - err := k.cdc.Unmarshal(value, &result) - if err != nil { + pageRes, err := query.Paginate(accountStore, req.Pagination, func(key, value []byte) error { + var amount sdk.Int + if err := amount.Unmarshal(value); err != nil { return err } - balances = append(balances, result) + balances = append(balances, sdk.NewCoin(string(key), amount)) return nil }) @@ -187,7 +186,7 @@ func (k BaseKeeper) DenomOwners( req.Pagination, func(key []byte, value []byte, accumulate bool) (bool, error) { if accumulate { - address, err := types.AddressFromBalancesStore(key) + address, _, err := types.AddressAndDenomFromBalancesStore(key) if err != nil { return false, err } diff --git a/x/bank/keeper/send.go b/x/bank/keeper/send.go index 1d43d1a0e1d3..17427cabba7c 100644 --- a/x/bank/keeper/send.go +++ b/x/bank/keeper/send.go @@ -243,8 +243,11 @@ func (k BaseSendKeeper) initBalances(ctx sdk.Context, addr sdk.AccAddress, balan // x/bank invariants prohibit persistence of zero balances if !balance.IsZero() { - bz := k.cdc.MustMarshal(&balance) - accountStore.Set([]byte(balance.Denom), bz) + amount, err := balance.Amount.Marshal() + if err != nil { + return err + } + accountStore.Set([]byte(balance.Denom), amount) denomPrefixStore, ok := denomPrefixStores[balance.Denom] if !ok { @@ -278,8 +281,11 @@ func (k BaseSendKeeper) setBalance(ctx sdk.Context, addr sdk.AccAddress, balance accountStore.Delete([]byte(balance.Denom)) denomPrefixStore.Delete(address.MustLengthPrefix(addr)) } else { - bz := k.cdc.MustMarshal(&balance) - accountStore.Set([]byte(balance.Denom), bz) + amount, err := balance.Amount.Marshal() + if err != nil { + return err + } + accountStore.Set([]byte(balance.Denom), amount) // Store a reverse index from denomination to account address with a // sentinel value. diff --git a/x/bank/keeper/view.go b/x/bank/keeper/view.go index 73334ef37035..62fed93c8747 100644 --- a/x/bank/keeper/view.go +++ b/x/bank/keeper/view.go @@ -98,16 +98,17 @@ func (k BaseViewKeeper) GetAccountsBalances(ctx sdk.Context) []types.Balance { // by address. func (k BaseViewKeeper) GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin { accountStore := k.getAccountStore(ctx, addr) - + amount := sdk.ZeroInt() bz := accountStore.Get([]byte(denom)) if bz == nil { - return sdk.NewCoin(denom, sdk.ZeroInt()) + return sdk.NewCoin(denom, amount) } - var balance sdk.Coin - k.cdc.MustUnmarshal(bz, &balance) + if err := amount.Unmarshal(bz); err != nil { + panic(err) + } - return balance + return sdk.NewCoin(denom, amount) } // IterateAccountBalances iterates over the balances of a single account and @@ -120,10 +121,12 @@ func (k BaseViewKeeper) IterateAccountBalances(ctx sdk.Context, addr sdk.AccAddr defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - var balance sdk.Coin - k.cdc.MustUnmarshal(iterator.Value(), &balance) + var amount sdk.Int + if err := amount.Unmarshal(iterator.Value()); err != nil { + panic(err) + } - if cb(balance) { + if cb(sdk.NewCoin(string(iterator.Key()), amount)) { break } } @@ -140,7 +143,7 @@ func (k BaseViewKeeper) IterateAllBalances(ctx sdk.Context, cb func(sdk.AccAddre defer iterator.Close() for ; iterator.Valid(); iterator.Next() { - address, err := types.AddressFromBalancesStore(iterator.Key()) + address, denom, err := types.AddressAndDenomFromBalancesStore(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 @@ -148,10 +151,12 @@ func (k BaseViewKeeper) IterateAllBalances(ctx sdk.Context, cb func(sdk.AccAddre panic(err) } - var balance sdk.Coin - k.cdc.MustUnmarshal(iterator.Value(), &balance) + var amount sdk.Int + if err := amount.Unmarshal(iterator.Value()); err != nil { + panic(err) + } - if cb(address, balance) { + if cb(address, sdk.NewCoin(denom, amount)) { break } } diff --git a/x/bank/migrations/v044/store.go b/x/bank/migrations/v044/store.go index 58966450f189..faaa07a9068a 100644 --- a/x/bank/migrations/v044/store.go +++ b/x/bank/migrations/v044/store.go @@ -6,11 +6,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" v043 "github.com/cosmos/cosmos-sdk/x/bank/migrations/v043" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) // MigrateStore performs in-place store migrations from v0.43 to v0.44. The // migration includes: // +// - Migrate coin storage to save only amount. // - Add an additional reverse index from denomination to address. func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { store := ctx.KVStore(storeKey) @@ -36,6 +38,19 @@ func addDenomReverseIndex(store sdk.KVStore, cdc codec.BinaryCodec) error { return err } + var coin sdk.DecCoin + if err := cdc.Unmarshal(oldBalancesIter.Value(), &coin); err != nil { + return err + } + + bz, err := coin.Amount.Marshal() + if err != nil { + return err + } + + newStore := prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr)) + newStore.Set([]byte(coin.Denom), bz) + denomPrefixStore, ok := denomPrefixStores[balance.Denom] if !ok { denomPrefixStore = prefix.NewStore(store, CreateAddressDenomPrefix(balance.Denom)) diff --git a/x/bank/migrations/v044/store_test.go b/x/bank/migrations/v044/store_test.go index ac0958869f3e..61028e389440 100644 --- a/x/bank/migrations/v044/store_test.go +++ b/x/bank/migrations/v044/store_test.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/address" v043 "github.com/cosmos/cosmos-sdk/x/bank/migrations/v043" v044 "github.com/cosmos/cosmos-sdk/x/bank/migrations/v044" + "github.com/cosmos/cosmos-sdk/x/bank/types" ) func TestMigrateStore(t *testing.T) { @@ -37,6 +38,14 @@ func TestMigrateStore(t *testing.T) { require.NoError(t, v044.MigrateStore(ctx, bankKey, encCfg.Codec)) + for _, b := range balances { + addrPrefixStore := prefix.NewStore(store, types.CreateAccountBalancesPrefix(addr)) + bz := addrPrefixStore.Get([]byte(b.Denom)) + var expected sdk.Int + require.NoError(t, expected.Unmarshal(bz)) + require.Equal(t, expected, b.Amount) + } + for _, b := range balances { denomPrefixStore := prefix.NewStore(store, v044.CreateAddressDenomPrefix(b.Denom)) bz := denomPrefixStore.Get(address.MustLengthPrefix(addr)) diff --git a/x/bank/types/key.go b/x/bank/types/key.go index be8f43f9aebe..e3b6aa87afa3 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -37,26 +37,25 @@ func DenomMetadataKey(denom string) []byte { return append(DenomMetadataPrefix, d...) } -// AddressFromBalancesStore returns an account address from a balances prefix +// 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, AddressFromBalancesStore returns ErrInvalidKey. -func AddressFromBalancesStore(key []byte) (sdk.AccAddress, error) { +// If invalid key is passed, AddressAndDenomFromBalancesStore returns ErrInvalidKey. +func AddressAndDenomFromBalancesStore(key []byte) (sdk.AccAddress, string, error) { if len(key) == 0 { - return nil, ErrInvalidKey + return nil, "", ErrInvalidKey } kv.AssertKeyAtLeastLength(key, 1) - addrLen := key[0] - bound := int(addrLen) + addrBound := int(key[0]) - if len(key)-1 < bound { - return nil, ErrInvalidKey + if len(key)-1 < addrBound { + return nil, "", ErrInvalidKey } - return key[1 : bound+1], nil + return key[1 : addrBound+1], string(key[addrBound+1:]), 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 9a7f457e45bd..fadadd022097 100644 --- a/x/bank/types/key_test.go +++ b/x/bank/types/key_test.go @@ -42,7 +42,7 @@ func TestAddressFromBalancesStore(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - addr, err := types.AddressFromBalancesStore(tc.key) + addr, denom, err := types.AddressAndDenomFromBalancesStore(tc.key) if tc.wantErr { assert.Error(t, err) assert.True(t, errors.Is(types.ErrInvalidKey, err)) @@ -51,6 +51,7 @@ func TestAddressFromBalancesStore(t *testing.T) { } if len(tc.expectedKey) > 0 { assert.Equal(t, tc.expectedKey, addr) + assert.Equal(t, "stake", denom) } }) } From 5a47154f6c05e2783a109678c7d3b3c1a00b24df Mon Sep 17 00:00:00 2001 From: Anil Kumar Kammari Date: Fri, 6 Aug 2021 01:30:08 +0530 Subject: [PATCH 15/31] feat: Add backup option for cosmovisor (#9652) ## Description Ref: https://github.com/cosmos/cosmos-sdk/issues/9616#issuecomment-873051972 depends: #8590 This PR adds a full backup option for cosmovisor. `UNSAFE_SKIP_BACKUP` is an `env` setting introduced newly. - if `false` (default, **recommended**), cosmovisor will try to take backup and then upgrade. In case of failure while taking backup, it will just halt the process there and won't try the upgrade. - If `true`, the cosmovisor will try to upgrade without any backup. This setting makes it hard to recover from a failed upgrade. Node operators either need to sync from a healthy node or use a snapshot from others. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- cosmovisor/README.md | 1 + cosmovisor/args.go | 3 ++ cosmovisor/cmd/cosmovisor/main.go | 1 + cosmovisor/process.go | 53 +++++++++++++++++++++++++++++++ cosmovisor/process_test.go | 4 +-- 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/cosmovisor/README.md b/cosmovisor/README.md index e263966a49a0..5b3aa5d2cb11 100644 --- a/cosmovisor/README.md +++ b/cosmovisor/README.md @@ -22,6 +22,7 @@ All arguments passed to `cosmovisor` will be passed to the application binary (a * `DAEMON_NAME` is the name of the binary itself (e.g. `gaiad`, `regend`, `simd`, etc.). * `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*), if set to `true`, will enable auto-downloading of new binaries (for security reasons, this is intended for full nodes rather than validators). By default, `cosmovisor` will not auto-download new binaries. * `DAEMON_RESTART_AFTER_UPGRADE` (*optional*), if set to `true`, will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. By default, `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note that `cosmovisor` will not auto-restart the subprocess if there was an error. +* `UNSAFE_SKIP_BACKUP` (defaults to `false`), if set to `false`, will backup the data before trying the upgrade. Otherwise it will upgrade directly without doing any backup. This is useful (and recommended) in case of failures and when needed to rollback. It is advised to use backup option, i.e., `UNSAFE_SKIP_BACKUP=false` ## Folder Layout diff --git a/cosmovisor/args.go b/cosmovisor/args.go index 60cbb7d60167..c5b4d71af58c 100644 --- a/cosmovisor/args.go +++ b/cosmovisor/args.go @@ -24,6 +24,7 @@ type Config struct { AllowDownloadBinaries bool RestartAfterUpgrade bool LogBufferSize int + UnsafeSkipBackup bool } // Root returns the root directory where all info lives @@ -113,6 +114,8 @@ func GetConfigFromEnv() (*Config, error) { cfg.LogBufferSize = bufio.MaxScanTokenSize } + cfg.UnsafeSkipBackup = os.Getenv("UNSAFE_SKIP_BACKUP") == "true" + if err := cfg.validate(); err != nil { return nil, err } diff --git a/cosmovisor/cmd/cosmovisor/main.go b/cosmovisor/cmd/cosmovisor/main.go index a165acab38f6..f02f1190d522 100644 --- a/cosmovisor/cmd/cosmovisor/main.go +++ b/cosmovisor/cmd/cosmovisor/main.go @@ -22,6 +22,7 @@ func Run(args []string) error { } doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) + // if RestartAfterUpgrade, we launch after a successful upgrade (only condition LaunchProcess returns nil) for cfg.RestartAfterUpgrade && err == nil && doUpgrade { doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) diff --git a/cosmovisor/process.go b/cosmovisor/process.go index 6a67f65e162e..2058c72f4384 100644 --- a/cosmovisor/process.go +++ b/cosmovisor/process.go @@ -2,15 +2,21 @@ package cosmovisor import ( "bufio" + "encoding/json" "fmt" "io" + "io/ioutil" "log" "os" "os/exec" "os/signal" + "path/filepath" "strings" "sync" "syscall" + "time" + + "github.com/otiai10/copy" ) // LaunchProcess runs a subprocess and returns when the subprocess exits, @@ -70,12 +76,59 @@ func LaunchProcess(cfg *Config, args []string, stdout, stderr io.Writer) (bool, } if upgradeInfo != nil { + if err := doBackup(cfg); err != nil { + return false, err + } + return true, DoUpgrade(cfg, upgradeInfo) } return false, nil } +func doBackup(cfg *Config) error { + // take backup if `UNSAFE_SKIP_BACKUP` is not set. + if !cfg.UnsafeSkipBackup { + // check if upgrade-info.json is not empty. + var uInfo UpgradeInfo + upgradeInfoFile, err := ioutil.ReadFile(filepath.Join(cfg.Home, "data", "upgrade-info.json")) + if err != nil { + return fmt.Errorf("error while reading upgrade-info.json: %w", err) + } + + err = json.Unmarshal(upgradeInfoFile, &uInfo) + if err != nil { + return err + } + + if uInfo.Name == "" { + return fmt.Errorf("upgrade-info.json is empty") + } + + // a destination directory, Format YYYY-MM-DD + st := time.Now() + stStr := fmt.Sprintf("%d-%d-%d", st.Year(), st.Month(), st.Day()) + dst := filepath.Join(cfg.Home, fmt.Sprintf("data"+"-backup-%s", stStr)) + + fmt.Printf("starting to take backup of data directory at time %s", st) + + // copy the $DAEMON_HOME/data to a backup dir + err = copy.Copy(filepath.Join(cfg.Home, "data"), dst) + + if err != nil { + return fmt.Errorf("error while taking data backup: %w", err) + } + + // backup is done, lets check endtime to calculate total time taken for backup process + et := time.Now() + timeTaken := et.Sub(st) + fmt.Printf("backup saved at location: %s, completed at time: %s\n"+ + "time taken to complete the backup: %s", dst, et, timeTaken) + } + + return nil +} + // WaitResult is used to wrap feedback on cmd state with some mutex logic. // This is needed as multiple go-routines can affect this - two read pipes that can trigger upgrade // As well as the command, which can fail diff --git a/cosmovisor/process_test.go b/cosmovisor/process_test.go index 6dc964f21ee0..0b966b22bc64 100644 --- a/cosmovisor/process_test.go +++ b/cosmovisor/process_test.go @@ -23,7 +23,7 @@ func TestProcessTestSuite(t *testing.T) { // and args are passed through func (s *processTestSuite) TestLaunchProcess() { home := copyTestData(s.T(), "validate") - cfg := &cosmovisor.Config{Home: home, Name: "dummyd"} + cfg := &cosmovisor.Config{Home: home, Name: "dummyd", UnsafeSkipBackup: true} // should run the genesis binary and produce expected output var stdout, stderr bytes.Buffer @@ -65,7 +65,7 @@ func (s *processTestSuite) TestLaunchProcessWithDownloads() { // zip_binary -> "chain3" = ref_zipped -> zip_directory // zip_directory no upgrade home := copyTestData(s.T(), "download") - cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true} + cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true, UnsafeSkipBackup: true} // should run the genesis binary and produce expected output var stdout, stderr bytes.Buffer From 6b24ada74355a1e28b20ab45d9637c3b23485d36 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 6 Aug 2021 10:23:13 -0400 Subject: [PATCH 16/31] refactor: update default pruning strategy (#9859) The `default` pruning strategy, specifically the total number of recent blocks to keep (currently 100), was decided on completely arbitrary. I propose we change it to be enough blocks given that the typical block time is 5s and typical unbonding period is 21 days: (3600*(24/5)) * 21 = 362880. I propose this because anyone wishing to run an IBC relayer, which will be most chains, will have to be required to use `custom` to set it to this value anyway. So why not just make it the default and make it easier for operators. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- store/types/pruning.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/store/types/pruning.go b/store/types/pruning.go index 823da76c6585..4419acb950d8 100644 --- a/store/types/pruning.go +++ b/store/types/pruning.go @@ -11,10 +11,12 @@ const ( ) var ( - // PruneDefault defines a pruning strategy where the last 100 heights are kept - // in addition to every 100th and where to-be pruned heights are pruned at - // every 10th height. - PruneDefault = NewPruningOptions(100, 100, 10) + // PruneDefault defines a pruning strategy where the last 362880 heights are + // kept in addition to every 100th and where to-be pruned heights are pruned + // at every 10th height. The last 362880 heights are kept assuming the typical + // block time is 5s and typical unbonding period is 21 days. If these values + // do not match the applications' requirements, use the "custom" option. + PruneDefault = NewPruningOptions(362880, 100, 10) // PruneEverything defines a pruning strategy where all committed heights are // deleted, storing only the current height and where to-be pruned heights are From 703158955f3c551e7ed9eb7f12cb59742eddbdf3 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Fri, 6 Aug 2021 12:09:58 -0400 Subject: [PATCH 17/31] chore: add missing changelog entry from #9859 (#9867) add missing changelog entry from #9859 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e023cc18500..630275aa8a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Client Breaking Changes +* [\#9859](https://github.com/cosmos/cosmos-sdk/pull/9859) The `default` pruning strategy now keeps the last 362880 blocks instead of 100. 362880 equates to roughly enough blocks to cover the entire unbonding period assuming a 21 day unbonding period and 5s block time. * [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints. * [\#9781](https://github.com/cosmos/cosmos-sdk/pull/9781) Improve`withdraw-all-rewards` UX when broadcast mode `async` or `async` is used. * [\#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) Missing coin denomination in logs From 40bb2f4f9abbf30419a9b51d3ff153d215fff2e4 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Fri, 6 Aug 2021 18:54:13 +0200 Subject: [PATCH 18/31] chore: prepare Cosmovisor v0.1.0 release (#9868) ## Description Release Notes + Changelog --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- cosmovisor/CHANGELOG.md | 44 +++++++++++++++++++++++++++++++++++++ cosmovisor/README.md | 4 ++++ cosmovisor/RELEASE_NOTES.md | 8 +++++++ 3 files changed, 56 insertions(+) create mode 100644 cosmovisor/CHANGELOG.md create mode 100644 cosmovisor/RELEASE_NOTES.md diff --git a/cosmovisor/CHANGELOG.md b/cosmovisor/CHANGELOG.md new file mode 100644 index 000000000000..a8340ecb52e8 --- /dev/null +++ b/cosmovisor/CHANGELOG.md @@ -0,0 +1,44 @@ + + +# Changelog + +## v0.1 2021-08-06 + +This is the first release and we started this changelog on 2021-07-01. See the (README)[https://github.com/cosmos/cosmos-sdk/blob/release/cosmovisor/v0.1.x/cosmovisor/CHANGELOG.md] file for the full list of features. + +## Features + +* [\#9652](https://github.com/cosmos/cosmos-sdk/pull/9652) Add backup option for cosmovisor. diff --git a/cosmovisor/README.md b/cosmovisor/README.md index 5b3aa5d2cb11..678070749afa 100644 --- a/cosmovisor/README.md +++ b/cosmovisor/README.md @@ -4,6 +4,10 @@ *Note: If new versions of the application are not set up to run in-place store migrations, migrations will need to be run manually before restarting `cosmovisor` with the new binary. For this reason, we recommend applications adopt in-place store migrations.* +## Contributing + +Release branches has the following format `release/cosmovisor/vA.B.x`, where A and B are a number (eg: `release/cosmovisor/v0.1.x`). Releases are tagged using the following format: `cosmovisor/vA.B.C`. + ## Installation To install `cosmovisor`, run the following command: diff --git a/cosmovisor/RELEASE_NOTES.md b/cosmovisor/RELEASE_NOTES.md new file mode 100644 index 000000000000..e59981ee7ba4 --- /dev/null +++ b/cosmovisor/RELEASE_NOTES.md @@ -0,0 +1,8 @@ +# Cosmovisor v0.1.0 Release Notes + +This is the first tracked release of Cosmovisor. It contains the original behavior of scanning app stdin and stdout. +Since the original design, this release contains one important feature: state backup. Since v0.1, by default, cosmovisor will make a state backup (`/data` directory). Backup will be skipped if `UNSAFE_SKIP_BACKUP=true` is set. + +Updates to this release will be pushed to `release/cosmovisor/v0.1.x` branch. + +Please see the [CHANGELOG](https://github.com/cosmos/cosmos-sdk/blob/release/cosmovisor/v0.1.x/cosmovisor/CHANGELOG.md) for more details. From 2627a96a496e3c47d97b5c34720a7359600b70ed Mon Sep 17 00:00:00 2001 From: Ryan Christoffersen <12519942+ryanchristo@users.noreply.github.com> Date: Mon, 9 Aug 2021 01:09:17 -0700 Subject: [PATCH 19/31] docs: fix module spec ordering (#9870) ## Description Closes: #9858 This pull request fixes the ordering of module spec documents. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct `docs:` prefix in the PR title - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the [documentation writing guidelines](https://github.com/cosmos/cosmos-sdk/blob/master/docs/DOC_WRITING_GUIDELINES.md) - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct `docs:` prefix in the PR title - [ ] confirmed all author checklist items have been addressed - [ ] confirmed that this PR only changes documentation - [ ] reviewed content for consistency - [ ] reviewed content for thoroughness - [ ] reviewed content for spelling and grammar - [ ] tested instructions (if applicable) --- x/README.md | 4 ++-- x/auth/spec/04_keepers.md | 2 +- x/auth/spec/05_vesting.md | 2 +- x/auth/spec/{07_params.md => 06_params.md} | 2 +- x/mint/spec/01_concepts.md | 2 +- x/slashing/spec/README.md | 2 +- x/staking/spec/05_end_block.md | 2 +- x/staking/spec/06_hooks.md | 2 +- x/staking/spec/07_events.md | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) rename x/auth/spec/{07_params.md => 06_params.md} (98%) diff --git a/x/README.md b/x/README.md index 8e2505c216d1..86ff7e4823c8 100644 --- a/x/README.md +++ b/x/README.md @@ -1,6 +1,5 @@ # List of Modules @@ -14,6 +13,7 @@ Here are some production-grade modules that can be used in Cosmos SDK applicatio - [Crisis](crisis/spec/README.md) - Halting the blockchain under certain circumstances (e.g. if an invariant is broken). - [Distribution](distribution/spec/README.md) - Fee distribution, and staking token provision distribution. - [Evidence](evidence/spec/README.md) - Evidence handling for double signing, misbehaviour, etc. +- [Feegrant](feegrant/spec/README.md) - Grant fee allowances for executing transactions. - [Governance](gov/spec/README.md) - On-chain proposals and voting. - [Mint](mint/spec/README.md) - Creation of new units of staking token. - [Params](params/spec/README.md) - Globally available parameter store. diff --git a/x/auth/spec/04_keepers.md b/x/auth/spec/04_keepers.md index 8e4466cd68ab..40882e24e571 100644 --- a/x/auth/spec/04_keepers.md +++ b/x/auth/spec/04_keepers.md @@ -1,5 +1,5 @@ # Keepers diff --git a/x/auth/spec/05_vesting.md b/x/auth/spec/05_vesting.md index 214db97d15e6..b15526ba60b6 100644 --- a/x/auth/spec/05_vesting.md +++ b/x/auth/spec/05_vesting.md @@ -1,5 +1,5 @@ # Vesting diff --git a/x/auth/spec/07_params.md b/x/auth/spec/06_params.md similarity index 98% rename from x/auth/spec/07_params.md rename to x/auth/spec/06_params.md index 1bf24e7f6d6e..414e1888f1c6 100644 --- a/x/auth/spec/07_params.md +++ b/x/auth/spec/06_params.md @@ -1,5 +1,5 @@ # Parameters diff --git a/x/mint/spec/01_concepts.md b/x/mint/spec/01_concepts.md index e17e7845e801..38cfb8acdf70 100644 --- a/x/mint/spec/01_concepts.md +++ b/x/mint/spec/01_concepts.md @@ -1,5 +1,5 @@ # Concepts diff --git a/x/slashing/spec/README.md b/x/slashing/spec/README.md index 226306562333..fe175b100cf5 100644 --- a/x/slashing/spec/README.md +++ b/x/slashing/spec/README.md @@ -1,5 +1,5 @@ # End-Block diff --git a/x/staking/spec/06_hooks.md b/x/staking/spec/06_hooks.md index 655431a7e6ea..000b0050fcb9 100644 --- a/x/staking/spec/06_hooks.md +++ b/x/staking/spec/06_hooks.md @@ -1,5 +1,5 @@ # Hooks diff --git a/x/staking/spec/07_events.md b/x/staking/spec/07_events.md index f219fa2ca4c1..f2dc9934a2d6 100644 --- a/x/staking/spec/07_events.md +++ b/x/staking/spec/07_events.md @@ -1,5 +1,5 @@ # Events From 1cc93d2f881e24c9f601e89fdc5b21c1ce6a0d25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 08:22:51 +0000 Subject: [PATCH 20/31] build(deps): bump google.golang.org/grpc from 1.39.0 to 1.39.1 (#9872) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.39.0 to 1.39.1.

Release notes

Sourced from google.golang.org/grpc's releases.

Release 1.39.1

  • server: fix bug that net.Conn is leaked if the connection is closed (io.EOF) immediately with no traffic (#4642)
  • transport: fix race in transport stream accessing s.recvCompress (#4627)
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/grpc&package-manager=go_modules&previous-version=1.39.0&new-version=1.39.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 356f6033aef5..360fff06fde3 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/tendermint/tm-db v0.6.4 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c - google.golang.org/grpc v1.39.0 + google.golang.org/grpc v1.39.1 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 nhooyr.io/websocket v1.8.6 // indirect From f479b515a8ce2353ab583525a029f7e68dad4e5f Mon Sep 17 00:00:00 2001 From: daeMOn Date: Mon, 9 Aug 2021 12:35:01 +0200 Subject: [PATCH 21/31] fix: file keyring fails to add/import/export keys when input is not stdin (fix #9566) (#9821) ## Description Add a test case to reproduce the issue described in #9566. The test currently fails, and I've pointed some possible solutions over https://github.com/cosmos/cosmos-sdk/issues/9566#issuecomment-889281861. But I feel this needs more works in order to provide a more robust solution. I'll keep poking at better options, but taking any pointers if anyone has ideas. --- client/context.go | 6 +- client/keys/add.go | 2 +- client/keys/add_test.go | 47 ++++++++++++- client/keys/export.go | 2 +- client/keys/export_test.go | 139 ++++++++++++++++++++++++------------- client/keys/import.go | 2 +- client/keys/import_test.go | 114 +++++++++++++++++++++++------- 7 files changed, 232 insertions(+), 80 deletions(-) diff --git a/client/context.go b/client/context.go index 1308823c8409..9f814517355a 100644 --- a/client/context.go +++ b/client/context.go @@ -1,6 +1,7 @@ package client import ( + "bufio" "encoding/json" "io" "os" @@ -68,7 +69,10 @@ func (ctx Context) WithKeyringOptions(opts ...keyring.Option) Context { // WithInput returns a copy of the context with an updated input. func (ctx Context) WithInput(r io.Reader) Context { - ctx.Input = r + // convert to a bufio.Reader to have a shared buffer between the keyring and the + // the Commands, ensuring a read from one advance the read pointer for the other. + // see https://github.com/cosmos/cosmos-sdk/issues/9566. + ctx.Input = bufio.NewReader(r) return ctx } diff --git a/client/keys/add.go b/client/keys/add.go index 4b2a1a0a55a8..d9713fd2d57a 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -82,12 +82,12 @@ Example: } func runAddCmdPrepare(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + buf := bufio.NewReader(clientCtx.Input) return runAddCmd(clientCtx, cmd, args, buf) } diff --git a/client/keys/add_test.go b/client/keys/add_test.go index 8e6bf7e5cce5..2108380588d3 100644 --- a/client/keys/add_test.go +++ b/client/keys/add_test.go @@ -18,6 +18,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/testutil" sdk "github.com/cosmos/cosmos-sdk/types" + bip39 "github.com/cosmos/go-bip39" ) func Test_runAddCmdBasic(t *testing.T) { @@ -30,7 +31,7 @@ func Test_runAddCmdBasic(t *testing.T) { kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) require.NoError(t, err) - clientCtx := client.Context{}.WithKeyringDir(kbHome) + clientCtx := client.Context{}.WithKeyringDir(kbHome).WithInput(mockIn) ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) t.Cleanup(func() { @@ -227,3 +228,47 @@ func Test_runAddCmdDryRun(t *testing.T) { }) } } + +func TestAddRecoverFileBackend(t *testing.T) { + cmd := AddKeyCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + kbHome := t.TempDir() + + clientCtx := client.Context{}.WithKeyringDir(kbHome).WithInput(mockIn) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + cmd.SetArgs([]string{ + "keyname1", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", cli.OutputFlag, OutputFormatText), + fmt.Sprintf("--%s=%s", flags.FlagKeyAlgorithm, string(hd.Secp256k1Type)), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendFile), + fmt.Sprintf("--%s", flagRecover), + }) + + keyringPassword := "12345678" + + entropySeed, err := bip39.NewEntropy(mnemonicEntropySize) + require.NoError(t, err) + + mnemonic, err := bip39.NewMnemonic(entropySeed) + require.NoError(t, err) + + mockIn.Reset(fmt.Sprintf("%s\n%s\n%s\n", mnemonic, keyringPassword, keyringPassword)) + require.NoError(t, cmd.ExecuteContext(ctx)) + + kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendFile, kbHome, mockIn) + require.NoError(t, err) + + t.Cleanup(func() { + mockIn.Reset(fmt.Sprintf("%s\n%s\n", keyringPassword, keyringPassword)) + _ = kb.Delete("keyname1") + }) + + mockIn.Reset(fmt.Sprintf("%s\n%s\n", keyringPassword, keyringPassword)) + info, err := kb.Key("keyname1") + require.NoError(t, err) + require.Equal(t, "keyname1", info.GetName()) +} diff --git a/client/keys/export.go b/client/keys/export.go index 7e9cb88ed633..13491b5e839a 100644 --- a/client/keys/export.go +++ b/client/keys/export.go @@ -31,11 +31,11 @@ FULLY AWARE OF THE RISKS. If you are unsure, you may want to do some research and export your keys in ASCII-armored encrypted format.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + buf := bufio.NewReader(clientCtx.Input) unarmored, _ := cmd.Flags().GetBool(flagUnarmoredHex) unsafe, _ := cmd.Flags().GetBool(flagUnsafe) diff --git a/client/keys/export_test.go b/client/keys/export_test.go index 4282d5d29b66..a63cf7f9b7f6 100644 --- a/client/keys/export_test.go +++ b/client/keys/export_test.go @@ -1,6 +1,7 @@ package keys import ( + "bufio" "context" "fmt" "testing" @@ -17,55 +18,95 @@ import ( ) func Test_runExportCmd(t *testing.T) { - cmd := ExportKeyCommand() - cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) - mockIn := testutil.ApplyMockIODiscardOutErr(cmd) - - // Now add a temporary keybase - kbHome := t.TempDir() - - // create a key - kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) - require.NoError(t, err) - t.Cleanup(func() { - kb.Delete("keyname1") // nolint:errcheck - }) - - path := sdk.GetConfig().GetFullBIP44Path() - _, err = kb.NewAccount("keyname1", testutil.TestMnemonic, "", path, hd.Secp256k1) - require.NoError(t, err) - - // Now enter password - args := []string{ - "keyname1", - fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), - fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + testCases := []struct { + name string + keyringBackend string + extraArgs []string + userInput string + mustFail bool + expectedOutput string + }{ + { + name: "--unsafe only must fail", + keyringBackend: keyring.BackendTest, + extraArgs: []string{"--unsafe"}, + mustFail: true, + }, + { + name: "--unarmored-hex must fail", + keyringBackend: keyring.BackendTest, + extraArgs: []string{"--unarmored-hex"}, + mustFail: true, + }, + { + name: "--unsafe --unarmored-hex fail with no user confirmation", + keyringBackend: keyring.BackendTest, + extraArgs: []string{"--unsafe", "--unarmored-hex"}, + userInput: "", + mustFail: true, + expectedOutput: "", + }, + { + name: "--unsafe --unarmored-hex succeed", + keyringBackend: keyring.BackendTest, + extraArgs: []string{"--unsafe", "--unarmored-hex"}, + userInput: "y\n", + mustFail: false, + expectedOutput: "2485e33678db4175dc0ecef2d6e1fc493d4a0d7f7ce83324b6ed70afe77f3485\n", + }, + { + name: "file keyring backend properly read password and user confirmation", + keyringBackend: keyring.BackendFile, + extraArgs: []string{"--unsafe", "--unarmored-hex"}, + // first 2 pass for creating the key, then unsafe export confirmation, then unlock keyring pass + userInput: "12345678\n12345678\ny\n12345678\n", + mustFail: false, + expectedOutput: "2485e33678db4175dc0ecef2d6e1fc493d4a0d7f7ce83324b6ed70afe77f3485\n", + }, } - mockIn.Reset("123456789\n123456789\n") - cmd.SetArgs(args) - - clientCtx := client.Context{}. - WithKeyringDir(kbHome). - WithKeyring(kb) - ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) - - require.NoError(t, cmd.ExecuteContext(ctx)) - - argsUnsafeOnly := append(args, "--unsafe") - cmd.SetArgs(argsUnsafeOnly) - require.Error(t, cmd.ExecuteContext(ctx)) - - argsUnarmoredHexOnly := append(args, "--unarmored-hex") - cmd.SetArgs(argsUnarmoredHexOnly) - require.Error(t, cmd.ExecuteContext(ctx)) - - argsUnsafeUnarmoredHex := append(args, "--unsafe", "--unarmored-hex") - cmd.SetArgs(argsUnsafeUnarmoredHex) - require.Error(t, cmd.ExecuteContext(ctx)) - - mockIn, mockOut := testutil.ApplyMockIO(cmd) - mockIn.Reset("y\n") - require.NoError(t, cmd.ExecuteContext(ctx)) - require.Equal(t, "2485e33678db4175dc0ecef2d6e1fc493d4a0d7f7ce83324b6ed70afe77f3485\n", mockOut.String()) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + kbHome := t.TempDir() + defaultArgs := []string{ + "keyname1", + fmt.Sprintf("--%s=%s", flags.FlagHome, kbHome), + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, tc.keyringBackend), + } + + cmd := ExportKeyCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + + cmd.SetArgs(append(defaultArgs, tc.extraArgs...)) + mockIn, mockOut := testutil.ApplyMockIO(cmd) + + mockIn.Reset(tc.userInput) + mockInBuf := bufio.NewReader(mockIn) + + // create a key + kb, err := keyring.New(sdk.KeyringServiceName(), tc.keyringBackend, kbHome, bufio.NewReader(mockInBuf)) + require.NoError(t, err) + t.Cleanup(func() { + kb.Delete("keyname1") // nolint:errcheck + }) + + path := sdk.GetConfig().GetFullBIP44Path() + _, err = kb.NewAccount("keyname1", testutil.TestMnemonic, "", path, hd.Secp256k1) + require.NoError(t, err) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb). + WithInput(mockInBuf) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + err = cmd.ExecuteContext(ctx) + if tc.mustFail { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.expectedOutput, mockOut.String()) + } + }) + } } diff --git a/client/keys/import.go b/client/keys/import.go index 2b7e230654ce..36662a8dd2e6 100644 --- a/client/keys/import.go +++ b/client/keys/import.go @@ -18,11 +18,11 @@ func ImportKeyCommand() *cobra.Command { Long: "Import a ASCII armored private key into the local keybase.", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - buf := bufio.NewReader(cmd.InOrStdin()) clientCtx, err := client.GetClientQueryContext(cmd) if err != nil { return err } + buf := bufio.NewReader(clientCtx.Input) bz, err := ioutil.ReadFile(args[1]) if err != nil { diff --git a/client/keys/import_test.go b/client/keys/import_test.go index ea84408c2df5..ac05ed567daa 100644 --- a/client/keys/import_test.go +++ b/client/keys/import_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + "os" "path/filepath" "testing" @@ -17,25 +18,50 @@ import ( ) func Test_runImportCmd(t *testing.T) { - cmd := ImportKeyCommand() - cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) - mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + testCases := []struct { + name string + keyringBackend string + userInput string + expectError bool + }{ + { + name: "test backend success", + keyringBackend: keyring.BackendTest, + // key armor passphrase + userInput: "123456789\n", + }, + { + name: "test backend fail with wrong armor pass", + keyringBackend: keyring.BackendTest, + userInput: "987654321\n", + expectError: true, + }, + { + name: "file backend success", + keyringBackend: keyring.BackendFile, + // key armor passphrase + keyring password x2 + userInput: "123456789\n12345678\n12345678\n", + }, + { + name: "file backend fail with wrong armor pass", + keyringBackend: keyring.BackendFile, + userInput: "987654321\n12345678\n12345678\n", + expectError: true, + }, + { + name: "file backend fail with wrong keyring pass", + keyringBackend: keyring.BackendFile, + userInput: "123465789\n12345678\n87654321\n", + expectError: true, + }, + { + name: "file backend fail with no keyring pass", + keyringBackend: keyring.BackendFile, + userInput: "123465789\n", + expectError: true, + }, + } - // Now add a temporary keybase - kbHome := t.TempDir() - kb, err := keyring.New(sdk.KeyringServiceName(), keyring.BackendTest, kbHome, mockIn) - - clientCtx := client.Context{}. - WithKeyringDir(kbHome). - WithKeyring(kb) - ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) - - require.NoError(t, err) - t.Cleanup(func() { - kb.Delete("keyname1") // nolint:errcheck - }) - - keyfile := filepath.Join(kbHome, "key.asc") armoredKey := `-----BEGIN TENDERMINT PRIVATE KEY----- salt: A790BB721D1C094260EA84F5E5B72289 kdf: bcrypt @@ -45,12 +71,48 @@ HbP+c6JmeJy9JXe2rbbF1QtCX1gLqGcDQPBXiCtFvP7/8wTZtVOPj8vREzhZ9ElO =f3l4 -----END TENDERMINT PRIVATE KEY----- ` - require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644)) - - mockIn.Reset("123456789\n") - cmd.SetArgs([]string{ - "keyname1", keyfile, - fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), - }) - require.NoError(t, cmd.ExecuteContext(ctx)) + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cmd := ImportKeyCommand() + cmd.Flags().AddFlagSet(Commands("home").PersistentFlags()) + mockIn := testutil.ApplyMockIODiscardOutErr(cmd) + + // Now add a temporary keybase + kbHome := t.TempDir() + kb, err := keyring.New(sdk.KeyringServiceName(), tc.keyringBackend, kbHome, nil) + + clientCtx := client.Context{}. + WithKeyringDir(kbHome). + WithKeyring(kb). + WithInput(mockIn) + ctx := context.WithValue(context.Background(), client.ClientContextKey, &clientCtx) + + require.NoError(t, err) + t.Cleanup(func() { + kb.Delete("keyname1") // nolint:errcheck + }) + + keyfile := filepath.Join(kbHome, "key.asc") + + require.NoError(t, ioutil.WriteFile(keyfile, []byte(armoredKey), 0644)) + + defer func() { + _ = os.RemoveAll(kbHome) + }() + + mockIn.Reset(tc.userInput) + cmd.SetArgs([]string{ + "keyname1", keyfile, + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, tc.keyringBackend), + }) + + err = cmd.ExecuteContext(ctx) + if tc.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } } From 1d07ea4c8c089cc88832480a5bd7b7abdb0a951a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Mon, 9 Aug 2021 16:50:01 +0200 Subject: [PATCH 22/31] feat: add tx fee event (#9860) ## Description Closes: #9857 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + types/events.go | 1 + x/auth/ante/fee.go | 5 +++ x/auth/client/testutil/suite.go | 70 +++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 630275aa8a08..6b8c4562929d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [\#9860](https://github.com/cosmos/cosmos-sdk/pull/9860) Emit transaction fee in ante handler fee decorator. The event type is `tx` and the attribute is `fee`. * [\#9776](https://github.com/cosmos/cosmos-sdk/pull/9776) Add flag `staking-bond-denom` to specify the staking bond denomination value when initializing a new chain. * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. diff --git a/types/events.go b/types/events.go index 27a0017635af..9c495a9da2a7 100644 --- a/types/events.go +++ b/types/events.go @@ -227,6 +227,7 @@ var ( AttributeKeyAccountSequence = "acc_seq" AttributeKeySignature = "signature" + AttributeKeyFee = "fee" EventTypeMessage = "message" diff --git a/x/auth/ante/fee.go b/x/auth/ante/fee.go index e183887d0fc9..b1d1d72a770e 100644 --- a/x/auth/ante/fee.go +++ b/x/auth/ante/fee.go @@ -117,6 +117,11 @@ func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo } } + events := sdk.Events{sdk.NewEvent(sdk.EventTypeTx, + sdk.NewAttribute(sdk.AttributeKeyFee, feeTx.GetFee().String()), + )} + ctx.EventManager().EmitEvents(events) + return next(ctx, tx, simulate) } diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index 666542e30e1a..fd4a472f841c 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -428,6 +428,76 @@ func (s *IntegrationTestSuite) TestCLIQueryTxCmdByEvents() { } } +func (s *IntegrationTestSuite) TestCLIQueryTxsCmdByEvents() { + val := s.network.Validators[0] + + account2, err := val.ClientCtx.Keyring.Key("newAccount2") + s.Require().NoError(err) + + sendTokens := sdk.NewInt64Coin(s.cfg.BondDenom, 10) + + // Send coins. + out, err := s.createBankMsg( + val, account2.GetAddress(), + sdk.NewCoins(sendTokens), + ) + s.Require().NoError(err) + var txRes sdk.TxResponse + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) + s.Require().NoError(s.network.WaitForNextBlock()) + + // Query the tx by hash to get the inner tx. + out, err = clitestutil.ExecTestCLICmd(val.ClientCtx, authcli.QueryTxCmd(), []string{txRes.TxHash, fmt.Sprintf("--%s=json", tmcli.OutputFlag)}) + s.Require().NoError(err) + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &txRes)) + + testCases := []struct { + name string + args []string + expectEmpty bool + }{ + { + "fee event happy case", + []string{ + fmt.Sprintf("--events=tx.fee=%s", + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10))).String()), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + false, + }, + { + "no matching fee event", + []string{ + fmt.Sprintf("--events=tx.fee=%s", + sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(0))).String()), + fmt.Sprintf("--%s=json", tmcli.OutputFlag), + }, + true, + }, + } + + for _, tc := range testCases { + tc := tc + s.Run(tc.name, func() { + cmd := authcli.QueryTxsByEventsCmd() + clientCtx := val.ClientCtx + + out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args) + s.Require().NoError(err) + + var result sdk.SearchTxsResult + s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &result)) + + if tc.expectEmpty { + s.Require().Equal(0, len(result.Txs)) + } else { + s.Require().NotEqual(0, len(result.Txs)) + s.Require().NotNil(result.Txs[0]) + } + }) + } +} + func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() { val1 := s.network.Validators[0] From 59f63a2a86f24fa0e26a58ad0a0d904198cc46c7 Mon Sep 17 00:00:00 2001 From: likhita-809 <78951027+likhita-809@users.noreply.github.com> Date: Mon, 9 Aug 2021 21:00:28 +0530 Subject: [PATCH 23/31] fix: overwrite sequence and account-number flags when used with offline=false (#9856) ## Description Closes: #9784 Overwrite --sequence and --account-number flags by default flag values, when used with offline=false in sign-batch command. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + x/auth/client/cli/tx_sign.go | 9 ++++++++- x/auth/client/testutil/suite.go | 15 ++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b8c4562929d..5ce1e54c0879 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* (cli) [\#9856](https://github.com/cosmos/cosmos-sdk/pull/9856) Overwrite `--sequence` and `--account-number` flags with default flag values when used with `offline=false` in `sign-batch` command. * (cli) [\#9593](https://github.com/cosmos/cosmos-sdk/pull/9593) Check if chain-id is blank before verifying signatures in multisign and error. * (cli) [\#9717](https://github.com/cosmos/cosmos-sdk/pull/9717) Added CLI flag `--output json/text` to `tx` cli commands. diff --git a/x/auth/client/cli/tx_sign.go b/x/auth/client/cli/tx_sign.go index 34483f9c935b..08abeaa05972 100644 --- a/x/auth/client/cli/tx_sign.go +++ b/x/auth/client/cli/tx_sign.go @@ -28,7 +28,7 @@ func GetSignBatchCommand() *cobra.Command { Long: `Sign batch files of transactions generated with --generate-only. The command processes list of transactions from file (one StdTx each line), generate signed transactions or signatures and print their JSON encoding, delimited by '\n'. -As the signatures are generated, the command updates the account sequence number accordingly. +As the signatures are generated, the command updates the account and sequence number accordingly. If the --signature-only flag is set, it will output the signature parts only. @@ -38,6 +38,9 @@ it is required to set such parameters manually. Note, invalid values will cause the transaction to fail. The sequence will be incremented automatically for each transaction that is signed. +If --account-number or --sequence flag is used when offline=false, they are ignored and +overwritten by the default flag values. + The --multisig= flag generates a signature on behalf of a multisig account key. It implies --signature-only. `, @@ -89,6 +92,10 @@ func makeSignBatchCmd() func(cmd *cobra.Command, args []string) error { } scanner := authclient.NewBatchScanner(txCfg, infile) + if !clientCtx.Offline { + txFactory = txFactory.WithAccountNumber(0).WithSequence(0) + } + for sequence := txFactory.Sequence(); scanner.Scan(); sequence++ { unsignedStdTx := scanner.Tx() txFactory = txFactory.WithSequence(sequence) diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index fd4a472f841c..e744916b4ff1 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -126,8 +126,21 @@ func (s *IntegrationTestSuite) TestCLISignBatch() { _, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), "--offline") s.Require().EqualError(err, "required flag(s) \"account-number\", \"sequence\" not set") + // sign-batch file - offline and sequence is set but account-number is not set + _, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagSequence, "1"), "--offline") + s.Require().EqualError(err, "required flag(s) \"account-number\" not set") + + // sign-batch file - offline and account-number is set but sequence is not set + _, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, "1"), "--offline") + s.Require().EqualError(err, "required flag(s) \"sequence\" not set") + + // sign-batch file - sequence and account-number are set when offline is false + res, err := TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), fmt.Sprintf("--%s=%s", flags.FlagSequence, "1"), fmt.Sprintf("--%s=%s", flags.FlagAccountNumber, "1")) + s.Require().NoError(err) + s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) + // sign-batch file - res, err := TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) + res, err = TxSignBatchExec(val.ClientCtx, val.Address, outputFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID)) s.Require().NoError(err) s.Require().Equal(3, len(strings.Split(strings.Trim(res.String(), "\n"), "\n"))) From 5f960b72e2a0a8573bd32d0b1ed95141cc2bea2c Mon Sep 17 00:00:00 2001 From: atheeshp <59333759+atheeshp@users.noreply.github.com> Date: Mon, 9 Aug 2021 21:48:19 +0530 Subject: [PATCH 24/31] feat: `--generate-only` and `--offline` flags can use keyname. (#9838) ## Description Closes: #9837 ref: #9407 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + client/context.go | 10 --- x/auth/client/testutil/suite.go | 111 ++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce1e54c0879..6c971535d828 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. * [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.acc_seq='/'`). +* [\#9837](https://github.com/cosmos/cosmos-sdk/issues/9837) `--generate-only` flag will accept the keyname now. ### API Breaking Changes diff --git a/client/context.go b/client/context.go index 9f814517355a..90987355381b 100644 --- a/client/context.go +++ b/client/context.go @@ -11,7 +11,6 @@ import ( "gopkg.in/yaml.v2" "github.com/gogo/protobuf/proto" - "github.com/pkg/errors" rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/cosmos/cosmos-sdk/codec" @@ -324,15 +323,6 @@ func GetFromFields(kr keyring.Keyring, from string, genOnly bool) (sdk.AccAddres return nil, "", 0, nil } - if genOnly { - addr, err := sdk.AccAddressFromBech32(from) - if err != nil { - return nil, "", 0, errors.Wrap(err, "must provide a valid Bech32 address in generate-only mode") - } - - return addr, "", 0, nil - } - var info keyring.Info if addr, err := sdk.AccAddressFromBech32(from); err == nil { info, err = kr.KeyByAddress(addr) diff --git a/x/auth/client/testutil/suite.go b/x/auth/client/testutil/suite.go index e744916b4ff1..5980e441f0b6 100644 --- a/x/auth/client/testutil/suite.go +++ b/x/auth/client/testutil/suite.go @@ -30,6 +30,7 @@ import ( "github.com/cosmos/cosmos-sdk/types/tx/signing" authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/client/cli" bankcli "github.com/cosmos/cosmos-sdk/x/bank/client/testutil" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) @@ -108,6 +109,116 @@ func (s *IntegrationTestSuite) TestCLIValidateSignatures() { s.Require().EqualError(err, "signatures validation failed") } +func (s *IntegrationTestSuite) TestCLISignGenOnly() { + val := s.network.Validators[0] + val2 := s.network.Validators[1] + + info, err := val.ClientCtx.Keyring.KeyByAddress(val.Address) + s.Require().NoError(err) + keyName := info.GetName() + + account, err := val.ClientCtx.AccountRetriever.GetAccount(val.ClientCtx, info.GetAddress()) + s.Require().NoError(err) + + sendTokens := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(10))) + args := []string{ + keyName, // from keyname + val2.Address.String(), + sendTokens.String(), + fmt.Sprintf("--%s=true", flags.FlagGenerateOnly), // shouldn't break if we use keyname with --generate-only flag + fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), + fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), + } + generatedStd, err := clitestutil.ExecTestCLICmd(val.ClientCtx, bank.NewSendTxCmd(), args) + s.Require().NoError(err) + opFile := testutil.WriteToNewTempFile(s.T(), generatedStd.String()) + + commonArgs := []string{ + fmt.Sprintf("--%s=%s", flags.FlagKeyringBackend, keyring.BackendTest), + fmt.Sprintf("--%s=%s", flags.FlagHome, strings.Replace(val.ClientCtx.HomeDir, "simd", "simcli", 1)), + fmt.Sprintf("--%s=%s", flags.FlagChainID, val.ClientCtx.ChainID), + } + + cases := []struct { + name string + args []string + expErr bool + errMsg string + }{ + { + "offline mode with account-number, sequence and keyname (valid)", + []string{ + opFile.Name(), + fmt.Sprintf("--%s=true", flags.FlagOffline), + fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), + fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), + fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), + }, + false, + "", + }, + { + "offline mode with account-number, sequence and address key (valid)", + []string{ + opFile.Name(), + fmt.Sprintf("--%s=true", flags.FlagOffline), + fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address.String()), + fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), + fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), + }, + false, + "", + }, + { + "offline mode without account-number and keyname (invalid)", + []string{ + opFile.Name(), + fmt.Sprintf("--%s=true", flags.FlagOffline), + fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), + fmt.Sprintf("--%s=%d", flags.FlagSequence, account.GetSequence()), + }, + true, + `required flag(s) "account-number" not set`, + }, + { + "offline mode without sequence and keyname (invalid)", + []string{ + opFile.Name(), + fmt.Sprintf("--%s=true", flags.FlagOffline), + fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), + fmt.Sprintf("--%s=%d", flags.FlagAccountNumber, account.GetAccountNumber()), + }, + true, + `required flag(s) "sequence" not set`, + }, + { + "offline mode without account-number, sequence and keyname (invalid)", + []string{ + opFile.Name(), + fmt.Sprintf("--%s=%s", flags.FlagFrom, keyName), + fmt.Sprintf("--%s=true", flags.FlagOffline), + }, + true, + `required flag(s) "account-number", "sequence" not set`, + }, + } + + for _, tc := range cases { + cmd := authcli.GetSignCommand() + tmcli.PrepareBaseCmd(cmd, "", "") + out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cmd, append(tc.args, commonArgs...)) + if tc.expErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + s.Require().NoError(err) + signedTx := testutil.WriteToNewTempFile(s.T(), out.String()) + _, err := TxBroadcastExec(val.ClientCtx, signedTx.Name()) + s.Require().NoError(err) + } + } +} + func (s *IntegrationTestSuite) TestCLISignBatch() { val := s.network.Validators[0] var sendTokens = sdk.NewCoins( From 47bfcba519c4784e7c7f3ae2d99f02a7c09ef3ba Mon Sep 17 00:00:00 2001 From: Alexander Kolesov Date: Mon, 9 Aug 2021 20:44:11 +0300 Subject: [PATCH 25/31] docs: Fix vesting doc. (#9883) ## Description Closes: https://github.com/cosmos/cosmos-sdk/issues/9882 Add info about PermanentLockedAccount. Add info about MsgCreateVestingAccount. --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [x] included comments for [documenting Go code](https://blog.golang.org/godoc) - [x] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [x] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [x] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] confirmed `!` in the type prefix if API or client breaking change - [x] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [x] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- x/auth/spec/05_vesting.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/x/auth/spec/05_vesting.md b/x/auth/spec/05_vesting.md index b15526ba60b6..858579b6f12c 100644 --- a/x/auth/spec/05_vesting.md +++ b/x/auth/spec/05_vesting.md @@ -39,7 +39,7 @@ are included, the vesting will occur over the specified number of periods. For all vesting accounts, the owner of the vesting account is able to delegate and undelegate from validators, however they cannot transfer coins to another -account until those coins are vested. This specification allows for three +account until those coins are vested. This specification allows for four different kinds of vesting: - Delayed vesting, where all coins are vested once `ET` is reached. @@ -53,13 +53,15 @@ vesting account in that coins can be released in staggered tranches. For example, a periodic vesting account could be used for vesting arrangements where coins are relased quarterly, yearly, or over any other function of tokens over time. +- Permanent locked vesting, where coins are locked forever. Coins in this account can +still be used for delegating and for governance votes even while locked. ## Note Vesting accounts can be initialized with some vesting and non-vesting coins. -The non-vesting coins would be immediately transferable. The current -specification does not allow for vesting accounts to be created with normal -messages after genesis. All vesting accounts must be created at genesis, or as +The non-vesting coins would be immediately transferable. DelayedVesting and +ContinuousVesting accounts can be created with normal messages after genesis. +Other types of vesting accounts must be created at genesis, or as part of a manual network upgrade. The current specification only allows for _unconditional_ vesting (ie. there is no possibility of reaching `ET` and having coins fail to vest). @@ -132,6 +134,10 @@ type ViewKeeper interface { } ``` +### PermanentLockedAccount + ++++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/proto/cosmos/vesting/v1beta1/vesting.proto#L78-L83 + ## Vesting Account Specification Given a vesting account, we define the following in the proceeding operations: @@ -614,3 +620,5 @@ linearly over time. all coins at a given time. - PeriodicVestingAccount: A vesting account implementation that vests coins according to a custom vesting schedule. +- PermanentLockedAccount: It does not ever release coins, locking them indefinitely. +Coins in this account can still be used for delegating and for governance votes even while locked. \ No newline at end of file From 84cc05b74f5aa658f150ed2f7cce0049e315a3b7 Mon Sep 17 00:00:00 2001 From: Sai Kumar <17549398+gsk967@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:25:15 +0530 Subject: [PATCH 26/31] fix: fix the issue of make proto-gen (#9854) ## Description Closes: #9811 Previously `make proto-gen` used the same container name for all cosmos based sdks, now it will choose container name dynamically based on project name --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 2 +- Makefile | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c971535d828..94efc4d653d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,9 +101,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9762](https://github.com/cosmos/cosmos-sdk/pull/9762) The init command uses the chain-id from the client config if --chain-id is not provided * [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. * [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Fixes capability initialization issue on tx revert by moving initialization logic to `InitChain` and `BeginBlock`. +* [\#9854](https://github.com/cosmos/cosmos-sdk/pull/9854) Fixed the `make proto-gen` to get dynamic container name based on project name for the cosmos based sdks. * [\#9829](https://github.com/cosmos/cosmos-sdk/pull/9829) Fixed Coin denom sorting not being checked during `Balance.Validate` check. Refactored the Validation logic to use `Coins.Validate` for `Balance.Coins`. - ### State Machine Breaking * (x/bank) [\#9611](https://github.com/cosmos/cosmos-sdk/pull/9611) Introduce a new index to act as a reverse index between a denomination and address allowing to query for diff --git a/Makefile b/Makefile index 9d4d589becc1..5f2e27bae54b 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ MOCKS_DIR = $(CURDIR)/tests/mocks HTTPS_GIT := https://github.com/cosmos/cosmos-sdk.git DOCKER := $(shell which docker) DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf +PROJECT_NAME = $(shell git remote get-url origin | xargs basename -s .git) export GO111MODULE = on @@ -327,17 +328,17 @@ benchmark: ### Linting ### ############################################################################### -containerMarkdownLintImage=tmknom/markdownlint -containerMarkdownLint=cosmos-sdk-markdownlint -containerMarkdownLintFix=cosmos-sdk-markdownlint-fix +markdownLintImage=tmknom/markdownlint +containerMarkdownLint=$(PROJECT_NAME)-markdownlint +containerMarkdownLintFix=$(PROJECT_NAME)-markdownlint-fix lint: golangci-lint run --out-format=tab - @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerMarkdownLint}$$"; then docker start -a $(containerMarkdownLint); else docker run --name $(containerMarkdownLint) -i -v "$(CURDIR):/work" $(containerMarkdownLintImage); fi + @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerMarkdownLint}$$"; then docker start -a $(containerMarkdownLint); else docker run --name $(containerMarkdownLint) -i -v "$(CURDIR):/work" $(markdownLintImage); fi lint-fix: golangci-lint run --fix --out-format=tab --issues-exit-code=0 - @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerMarkdownLintFix}$$"; then docker start -a $(containerMarkdownLintFix); else docker run --name $(containerMarkdownLintFix) -i -v "$(CURDIR):/work" $(containerMarkdownLintImage) . --fix; fi + @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerMarkdownLintFix}$$"; then docker start -a $(containerMarkdownLintFix); else docker run --name $(containerMarkdownLintFix) -i -v "$(CURDIR):/work" $(markdownLintImage) . --fix; fi .PHONY: lint lint-fix @@ -377,11 +378,12 @@ devdoc-update: ### Protobuf ### ############################################################################### -containerProtoVer=v0.2 -containerProtoImage=tendermintdev/sdk-proto-gen:$(containerProtoVer) -containerProtoGen=cosmos-sdk-proto-gen-$(containerProtoVer) -containerProtoGenSwagger=cosmos-sdk-proto-gen-swagger-$(containerProtoVer) -containerProtoFmt=cosmos-sdk-proto-fmt-$(containerProtoVer) +protoVer=v0.2 +protoImageName=tendermintdev/sdk-proto-gen:$(protoVer) +containerProtoGen=$(PROJECT_NAME)-proto-gen-$(protoVer) +containerProtoGenAny=$(PROJECT_NAME)-proto-gen-any-$(protoVer) +containerProtoGenSwagger=$(PROJECT_NAME)-proto-gen-swagger-$(protoVer) +containerProtoFmt=$(PROJECT_NAME)-proto-fmt-$(protoVer) proto-all: proto-format proto-lint proto-gen @@ -393,7 +395,8 @@ proto-gen: # This generates the SDK's custom wrapper for google.protobuf.Any. It should only be run manually when needed proto-gen-any: @echo "Generating Protobuf Any" - $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) sh ./scripts/protocgen-any.sh + @if docker ps -a --format '{{.Names}}' | grep -Eq "^${containerProtoGenAny}$$"; then docker start -a $(containerProtoGenAny); else docker run --name $(containerProtoGenAny) -v $(CURDIR):/workspace --workdir /workspace $(containerProtoImage) \ + sh ./scripts/protocgen-any.sh; fi proto-swagger-gen: @echo "Generating Protobuf Swagger" From 47a3f027358423405d1d7786d4231d0758831bca Mon Sep 17 00:00:00 2001 From: likhita-809 <78951027+likhita-809@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:48:47 +0530 Subject: [PATCH 27/31] docs: Cleanup SDK docs (#9889) * return MaxUint64 for Limit on InfiniteGasMeter and add a func to return GasLeft on GasMeter * change dead link to appropriate link Co-authored-by: Marko --- docs/intro/sdk-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/intro/sdk-design.md b/docs/intro/sdk-design.md index 5c8c26965650..f54404dda665 100644 --- a/docs/intro/sdk-design.md +++ b/docs/intro/sdk-design.md @@ -88,7 +88,7 @@ SDK modules are defined in the `x/` folder of the SDK. Some core modules include - `x/bank`: Used to enable tokens and token transfers. - `x/staking` + `x/slashing`: Used to build Proof-Of-Stake blockchains. -In addition to the already existing modules in `x/`, that anyone can use in their app, the SDK lets you build your own custom modules. You can check an [example of that in the tutorial](https://cosmos.network/docs/tutorial/keeper.html). +In addition to the already existing modules in `x/`, that anyone can use in their app, the SDK lets you build your own custom modules. You can check an [example of that in the tutorial](https://tutorials.cosmos.network/). ## Next {hide} From 055e7e8d80322e4bcac60063245962f8eb06e8d4 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 10 Aug 2021 08:44:58 -0400 Subject: [PATCH 28/31] feat: implement x/params get all subspaces/keys (#9884) --- CHANGELOG.md | 1 + docs/core/proto-docs.md | 50 +- proto/cosmos/params/v1beta1/query.proto | 22 + x/auth/types/query.pb.go | 4 +- x/group/types.pb.go | 6 +- x/params/keeper/grpc_query.go | 33 ++ x/params/keeper/grpc_query_test.go | 21 + x/params/keeper/keeper.go | 12 + x/params/keeper/keeper_test.go | 23 + x/params/types/proposal/query.pb.go | 618 +++++++++++++++++++++++- x/params/types/proposal/query.pb.gw.go | 62 +++ x/params/types/subspace.go | 16 + x/params/types/subspace_test.go | 36 ++ 13 files changed, 876 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94efc4d653d1..11184090ae53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* [\#9884](https://github.com/cosmos/cosmos-sdk/pull/9884) Provide a new gRPC query handler, `/cosmos/params/v1beta1/subspaces`, that allows the ability to query for all registered subspaces and their respective keys. * [\#9860](https://github.com/cosmos/cosmos-sdk/pull/9860) Emit transaction fee in ante handler fee decorator. The event type is `tx` and the attribute is `fee`. * [\#9776](https://github.com/cosmos/cosmos-sdk/pull/9776) Add flag `staking-bond-denom` to specify the staking bond denomination value when initializing a new chain. * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index 6b68c21ae232..b9d5372e35ab 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -483,6 +483,9 @@ - [cosmos/params/v1beta1/query.proto](#cosmos/params/v1beta1/query.proto) - [QueryParamsRequest](#cosmos.params.v1beta1.QueryParamsRequest) - [QueryParamsResponse](#cosmos.params.v1beta1.QueryParamsResponse) + - [QuerySubspacesRequest](#cosmos.params.v1beta1.QuerySubspacesRequest) + - [QuerySubspacesResponse](#cosmos.params.v1beta1.QuerySubspacesResponse) + - [Subspace](#cosmos.params.v1beta1.Subspace) - [Query](#cosmos.params.v1beta1.Query) @@ -966,7 +969,7 @@ Query defines the gRPC querier service. | `Accounts` | [QueryAccountsRequest](#cosmos.auth.v1beta1.QueryAccountsRequest) | [QueryAccountsResponse](#cosmos.auth.v1beta1.QueryAccountsResponse) | Accounts returns all the existing accounts | GET|/cosmos/auth/v1beta1/accounts| | `Account` | [QueryAccountRequest](#cosmos.auth.v1beta1.QueryAccountRequest) | [QueryAccountResponse](#cosmos.auth.v1beta1.QueryAccountResponse) | Account returns account details based on address. | GET|/cosmos/auth/v1beta1/accounts/{address}| | `Params` | [QueryParamsRequest](#cosmos.auth.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.auth.v1beta1.QueryParamsResponse) | Params queries all parameters. | GET|/cosmos/auth/v1beta1/params| -| `ModuleAccounts` | [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest) | [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse) | ModuleAccounts returns all the existing Module Accounts. | GET|/cosmos/auth/v1beta1/module_accounts| +| `ModuleAccounts` | [QueryModuleAccountsRequest](#cosmos.auth.v1beta1.QueryModuleAccountsRequest) | [QueryModuleAccountsResponse](#cosmos.auth.v1beta1.QueryModuleAccountsResponse) | ModuleAccounts returns all the existing module accounts. | GET|/cosmos/auth/v1beta1/module_accounts| @@ -6957,6 +6960,50 @@ QueryParamsResponse is response type for the Query/Params RPC method. + + + +### QuerySubspacesRequest +QuerySubspacesRequest defines a request type for querying for all registered +subspaces and all keys for a subspace. + + + + + + + + +### QuerySubspacesResponse +QuerySubspacesResponse defines the response types for querying for all +registered subspaces and all keys for a subspace. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `subspaces` | [Subspace](#cosmos.params.v1beta1.Subspace) | repeated | | + + + + + + + + +### Subspace +Subspace defines a parameter subspace name and all the keys that exist for +the subspace. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| `subspace` | [string](#string) | | | +| `keys` | [string](#string) | repeated | | + + + + + @@ -6972,6 +7019,7 @@ Query defines the gRPC querier service. | Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint | | ----------- | ------------ | ------------- | ------------| ------- | -------- | | `Params` | [QueryParamsRequest](#cosmos.params.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#cosmos.params.v1beta1.QueryParamsResponse) | Params queries a specific parameter of a module, given its subspace and key. | GET|/cosmos/params/v1beta1/params| +| `Subspaces` | [QuerySubspacesRequest](#cosmos.params.v1beta1.QuerySubspacesRequest) | [QuerySubspacesResponse](#cosmos.params.v1beta1.QuerySubspacesResponse) | Subspaces queries for all registered subspaces and all keys for a subspace. | GET|/cosmos/params/v1beta1/subspaces| diff --git a/proto/cosmos/params/v1beta1/query.proto b/proto/cosmos/params/v1beta1/query.proto index 1078e02ae3e6..3b1c9a760092 100644 --- a/proto/cosmos/params/v1beta1/query.proto +++ b/proto/cosmos/params/v1beta1/query.proto @@ -14,6 +14,11 @@ service Query { rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/cosmos/params/v1beta1/params"; } + + // Subspaces queries for all registered subspaces and all keys for a subspace. + rpc Subspaces(QuerySubspacesRequest) returns (QuerySubspacesResponse) { + option (google.api.http).get = "/cosmos/params/v1beta1/subspaces"; + } } // QueryParamsRequest is request type for the Query/Params RPC method. @@ -30,3 +35,20 @@ message QueryParamsResponse { // param defines the queried parameter. ParamChange param = 1 [(gogoproto.nullable) = false]; } + +// QuerySubspacesRequest defines a request type for querying for all registered +// subspaces and all keys for a subspace. +message QuerySubspacesRequest {} + +// QuerySubspacesResponse defines the response types for querying for all +// registered subspaces and all keys for a subspace. +message QuerySubspacesResponse { + repeated Subspace subspaces = 1; +} + +// Subspace defines a parameter subspace name and all the keys that exist for +// the subspace. +message Subspace { + string subspace = 1; + repeated string keys = 2; +} diff --git a/x/auth/types/query.pb.go b/x/auth/types/query.pb.go index 61c7041a6a9d..c3712a26cf79 100644 --- a/x/auth/types/query.pb.go +++ b/x/auth/types/query.pb.go @@ -456,7 +456,7 @@ type QueryClient interface { Account(ctx context.Context, in *QueryAccountRequest, opts ...grpc.CallOption) (*QueryAccountResponse, error) // Params queries all parameters. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) - // ModuleAccounts returns all the existing Module Accounts. + // ModuleAccounts returns all the existing module accounts. ModuleAccounts(ctx context.Context, in *QueryModuleAccountsRequest, opts ...grpc.CallOption) (*QueryModuleAccountsResponse, error) } @@ -512,7 +512,7 @@ type QueryServer interface { Account(context.Context, *QueryAccountRequest) (*QueryAccountResponse, error) // Params queries all parameters. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) - // ModuleAccounts returns all the existing Module Accounts. + // ModuleAccounts returns all the existing module accounts. ModuleAccounts(context.Context, *QueryModuleAccountsRequest) (*QueryModuleAccountsResponse, error) } diff --git a/x/group/types.pb.go b/x/group/types.pb.go index 56fbe2286f4f..c0e5cf2d5e75 100644 --- a/x/group/types.pb.go +++ b/x/group/types.pb.go @@ -567,9 +567,9 @@ type Proposal struct { Result Proposal_Result `protobuf:"varint,9,opt,name=result,proto3,enum=cosmos.group.v1beta1.Proposal_Result" json:"result,omitempty"` // vote_state contains the sums of all weighted votes for this proposal. VoteState Tally `protobuf:"bytes,10,opt,name=vote_state,json=voteState,proto3" json:"vote_state"` - // timeout is the timestamp of the block where the proposal execution times out. Header times of the votes and execution messages - // must be before this end time to be included in the election. After the timeout timestamp the proposal can not be - // executed anymore and should be considered pending delete. + // timeout is the timestamp of the block where the proposal execution times out. Header times of the votes and + // execution messages must be before this end time to be included in the election. After the timeout timestamp the + // proposal can not be executed anymore and should be considered pending delete. Timeout time.Time `protobuf:"bytes,11,opt,name=timeout,proto3,stdtime" json:"timeout"` // executor_result is the final result based on the votes and election rule. Initial value is NotRun. ExecutorResult Proposal_ExecutorResult `protobuf:"varint,12,opt,name=executor_result,json=executorResult,proto3,enum=cosmos.group.v1beta1.Proposal_ExecutorResult" json:"executor_result,omitempty"` diff --git a/x/params/keeper/grpc_query.go b/x/params/keeper/grpc_query.go index 2cfa1c8042d7..d918dc002a38 100644 --- a/x/params/keeper/grpc_query.go +++ b/x/params/keeper/grpc_query.go @@ -34,3 +34,36 @@ func (k Keeper) Params(c context.Context, req *proposal.QueryParamsRequest) (*pr return &proposal.QueryParamsResponse{Param: param}, nil } + +// Subspaces implements the gRPC query handler for fetching all registered +// subspaces and all the keys for each subspace. +func (k Keeper) Subspaces( + goCtx context.Context, + req *proposal.QuerySubspacesRequest, +) (*proposal.QuerySubspacesResponse, error) { + + if req == nil { + return nil, status.Errorf(codes.InvalidArgument, "empty request") + } + + spaces := k.GetSubspaces() + resp := &proposal.QuerySubspacesResponse{ + Subspaces: make([]*proposal.Subspace, len(spaces)), + } + + ctx := sdk.UnwrapSDKContext(goCtx) + for i, ss := range spaces { + var keys []string + ss.IterateKeys(ctx, func(key []byte) bool { + keys = append(keys, string(key)) + return false + }) + + resp.Subspaces[i] = &proposal.Subspace{ + Subspace: ss.Name(), + Keys: keys, + } + } + + return resp, nil +} diff --git a/x/params/keeper/grpc_query_test.go b/x/params/keeper/grpc_query_test.go index 19794beb0808..f6985fe7e6dd 100644 --- a/x/params/keeper/grpc_query_test.go +++ b/x/params/keeper/grpc_query_test.go @@ -84,3 +84,24 @@ func (suite *KeeperTestSuite) TestGRPCQueryParams() { }) } } + +func (suite *KeeperTestSuite) TestGRPCQuerySubspaces() { + ctx := sdk.WrapSDKContext(suite.ctx) + + // NOTE: Each subspace will not have any keys that we can check against + // because InitGenesis has not been called during app construction. + resp, err := suite.queryClient.Subspaces(ctx, &proposal.QuerySubspacesRequest{}) + suite.Require().NoError(err) + suite.Require().NotNil(resp) + + spaces := make([]string, len(resp.Subspaces)) + i := 0 + for _, ss := range resp.Subspaces { + spaces[i] = ss.Subspace + i++ + } + + // require the response contains a few subspaces we know exist + suite.Require().Contains(spaces, "bank") + suite.Require().Contains(spaces, "staking") +} diff --git a/x/params/keeper/keeper.go b/x/params/keeper/keeper.go index 388d99c51c99..8a0e708aeb3d 100644 --- a/x/params/keeper/keeper.go +++ b/x/params/keeper/keeper.go @@ -59,3 +59,15 @@ func (k Keeper) GetSubspace(s string) (types.Subspace, bool) { } return *space, ok } + +// GetSubspaces returns all the registered subspaces. +func (k Keeper) GetSubspaces() []types.Subspace { + spaces := make([]types.Subspace, len(k.spaces)) + i := 0 + for _, ss := range k.spaces { + spaces[i] = *ss + i++ + } + + return spaces +} diff --git a/x/params/keeper/keeper_test.go b/x/params/keeper/keeper_test.go index 44c8223f2c25..795a9aac6ffc 100644 --- a/x/params/keeper/keeper_test.go +++ b/x/params/keeper/keeper_test.go @@ -148,6 +148,29 @@ func indirect(ptr interface{}) interface{} { return reflect.ValueOf(ptr).Elem().Interface() } +func TestGetSubspaces(t *testing.T) { + _, _, _, _, keeper := testComponents() + + table := types.NewKeyTable( + types.NewParamSetPair([]byte("string"), "", validateNoOp), + types.NewParamSetPair([]byte("bool"), false, validateNoOp), + ) + + _ = keeper.Subspace("key1").WithKeyTable(table) + _ = keeper.Subspace("key2").WithKeyTable(table) + + spaces := keeper.GetSubspaces() + require.Len(t, spaces, 2) + + var names []string + for _, ss := range spaces { + names = append(names, ss.Name()) + } + + require.Contains(t, names, "key1") + require.Contains(t, names, "key2") +} + func TestSubspace(t *testing.T) { cdc, ctx, key, _, keeper := testComponents() diff --git a/x/params/types/proposal/query.pb.go b/x/params/types/proposal/query.pb.go index e0c770d70e24..229f4f8e9bb0 100644 --- a/x/params/types/proposal/query.pb.go +++ b/x/params/types/proposal/query.pb.go @@ -130,36 +130,182 @@ func (m *QueryParamsResponse) GetParam() ParamChange { return ParamChange{} } +// QuerySubspacesRequest defines a request type for querying for all registered +// subspaces and all keys for a subspace. +type QuerySubspacesRequest struct { +} + +func (m *QuerySubspacesRequest) Reset() { *m = QuerySubspacesRequest{} } +func (m *QuerySubspacesRequest) String() string { return proto.CompactTextString(m) } +func (*QuerySubspacesRequest) ProtoMessage() {} +func (*QuerySubspacesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_2b32979c1792ccc4, []int{2} +} +func (m *QuerySubspacesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySubspacesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySubspacesRequest.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 *QuerySubspacesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySubspacesRequest.Merge(m, src) +} +func (m *QuerySubspacesRequest) XXX_Size() int { + return m.Size() +} +func (m *QuerySubspacesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySubspacesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySubspacesRequest proto.InternalMessageInfo + +// QuerySubspacesResponse defines the response types for querying for all +// registered subspaces and all keys for a subspace. +type QuerySubspacesResponse struct { + Subspaces []*Subspace `protobuf:"bytes,1,rep,name=subspaces,proto3" json:"subspaces,omitempty"` +} + +func (m *QuerySubspacesResponse) Reset() { *m = QuerySubspacesResponse{} } +func (m *QuerySubspacesResponse) String() string { return proto.CompactTextString(m) } +func (*QuerySubspacesResponse) ProtoMessage() {} +func (*QuerySubspacesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2b32979c1792ccc4, []int{3} +} +func (m *QuerySubspacesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QuerySubspacesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QuerySubspacesResponse.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 *QuerySubspacesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QuerySubspacesResponse.Merge(m, src) +} +func (m *QuerySubspacesResponse) XXX_Size() int { + return m.Size() +} +func (m *QuerySubspacesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QuerySubspacesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QuerySubspacesResponse proto.InternalMessageInfo + +func (m *QuerySubspacesResponse) GetSubspaces() []*Subspace { + if m != nil { + return m.Subspaces + } + return nil +} + +// Subspace defines a parameter subspace name and all the keys that exist for +// the subspace. +type Subspace struct { + Subspace string `protobuf:"bytes,1,opt,name=subspace,proto3" json:"subspace,omitempty"` + Keys []string `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` +} + +func (m *Subspace) Reset() { *m = Subspace{} } +func (m *Subspace) String() string { return proto.CompactTextString(m) } +func (*Subspace) ProtoMessage() {} +func (*Subspace) Descriptor() ([]byte, []int) { + return fileDescriptor_2b32979c1792ccc4, []int{4} +} +func (m *Subspace) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Subspace) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Subspace.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 *Subspace) XXX_Merge(src proto.Message) { + xxx_messageInfo_Subspace.Merge(m, src) +} +func (m *Subspace) XXX_Size() int { + return m.Size() +} +func (m *Subspace) XXX_DiscardUnknown() { + xxx_messageInfo_Subspace.DiscardUnknown(m) +} + +var xxx_messageInfo_Subspace proto.InternalMessageInfo + +func (m *Subspace) GetSubspace() string { + if m != nil { + return m.Subspace + } + return "" +} + +func (m *Subspace) GetKeys() []string { + if m != nil { + return m.Keys + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "cosmos.params.v1beta1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "cosmos.params.v1beta1.QueryParamsResponse") + proto.RegisterType((*QuerySubspacesRequest)(nil), "cosmos.params.v1beta1.QuerySubspacesRequest") + proto.RegisterType((*QuerySubspacesResponse)(nil), "cosmos.params.v1beta1.QuerySubspacesResponse") + proto.RegisterType((*Subspace)(nil), "cosmos.params.v1beta1.Subspace") } func init() { proto.RegisterFile("cosmos/params/v1beta1/query.proto", fileDescriptor_2b32979c1792ccc4) } var fileDescriptor_2b32979c1792ccc4 = []byte{ - // 321 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0x31, 0x4b, 0x3b, 0x31, - 0x14, 0xc0, 0x2f, 0xfd, 0xff, 0x5b, 0x34, 0x2e, 0x12, 0x15, 0xca, 0xa1, 0x57, 0x3d, 0x10, 0x54, - 0x30, 0xa1, 0xd5, 0xd9, 0xa1, 0xee, 0xa2, 0x05, 0x17, 0xb7, 0x5c, 0x0d, 0x69, 0x69, 0x7b, 0x2f, - 0xbd, 0x97, 0x13, 0xbb, 0x3a, 0x38, 0x17, 0xfc, 0x52, 0x1d, 0x0b, 0x2e, 0x4e, 0x22, 0xad, 0x1f, - 0x44, 0x9a, 0xb4, 0x82, 0x58, 0xc5, 0x29, 0x2f, 0x2f, 0xbf, 0xf7, 0x7b, 0x79, 0x09, 0xdd, 0x6b, - 0x02, 0xf6, 0x00, 0x85, 0x91, 0x99, 0xec, 0xa1, 0xb8, 0xab, 0x26, 0xca, 0xca, 0xaa, 0xe8, 0xe7, - 0x2a, 0x1b, 0x70, 0x93, 0x81, 0x05, 0xb6, 0xe5, 0x11, 0xee, 0x11, 0x3e, 0x47, 0xc2, 0x4d, 0x0d, - 0x1a, 0x1c, 0x21, 0x66, 0x91, 0x87, 0xc3, 0x6d, 0x0d, 0xa0, 0xbb, 0x4a, 0x48, 0xd3, 0x16, 0x32, - 0x4d, 0xc1, 0x4a, 0xdb, 0x86, 0x14, 0xe7, 0xa7, 0xf1, 0xf2, 0x6e, 0x73, 0xb3, 0x63, 0xe2, 0x3a, - 0x65, 0x57, 0xb3, 0xee, 0x97, 0x2e, 0xd9, 0x50, 0xfd, 0x5c, 0xa1, 0x65, 0x21, 0x5d, 0xc1, 0x3c, - 0x41, 0x23, 0x9b, 0xaa, 0x4c, 0x76, 0xc9, 0xc1, 0x6a, 0xe3, 0x73, 0xcf, 0xd6, 0xe9, 0xbf, 0x8e, - 0x1a, 0x94, 0x0b, 0x2e, 0x3d, 0x0b, 0xe3, 0x6b, 0xba, 0xf1, 0xc5, 0x81, 0x06, 0x52, 0x54, 0xec, - 0x8c, 0x16, 0x5d, 0x2b, 0x67, 0x58, 0xab, 0xc5, 0x7c, 0xe9, 0x64, 0xdc, 0x55, 0x9d, 0xb7, 0x64, - 0xaa, 0x55, 0xfd, 0xff, 0xe8, 0xb5, 0x12, 0x34, 0x7c, 0x59, 0x6d, 0x48, 0x68, 0xd1, 0x79, 0xd9, - 0x23, 0xa1, 0x25, 0x2f, 0x67, 0x87, 0x3f, 0x58, 0xbe, 0x0f, 0x11, 0x1e, 0xfd, 0x05, 0xf5, 0x77, - 0x8d, 0xf7, 0x1f, 0x9e, 0xdf, 0x9f, 0x0a, 0x15, 0xb6, 0x23, 0x7e, 0x7b, 0xb3, 0xfa, 0xc5, 0x68, - 0x12, 0x91, 0xf1, 0x24, 0x22, 0x6f, 0x93, 0x88, 0x0c, 0xa7, 0x51, 0x30, 0x9e, 0x46, 0xc1, 0xcb, - 0x34, 0x0a, 0x6e, 0x4e, 0x75, 0xdb, 0xb6, 0xf2, 0x84, 0x37, 0xa1, 0xb7, 0x50, 0xf8, 0xe5, 0x18, - 0x6f, 0x3b, 0xe2, 0x7e, 0xe1, 0xb3, 0x03, 0xa3, 0x50, 0x98, 0x0c, 0x0c, 0xa0, 0xec, 0x26, 0x25, - 0xf7, 0x09, 0x27, 0x1f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xaa, 0xd5, 0x67, 0xc7, 0x18, 0x02, 0x00, - 0x00, + // 415 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0x4d, 0x8b, 0xd3, 0x40, + 0x18, 0xce, 0xa4, 0x1f, 0x34, 0xd3, 0x8b, 0x8c, 0x56, 0x43, 0xd0, 0x34, 0x0e, 0x08, 0x51, 0x6c, + 0x86, 0x56, 0x4f, 0x82, 0x1e, 0xea, 0x5d, 0x34, 0x22, 0x82, 0xb7, 0x49, 0x1d, 0xd2, 0xd2, 0x36, + 0x33, 0xcd, 0x24, 0x62, 0xae, 0x1e, 0x3c, 0x2f, 0xbb, 0xbf, 0x61, 0xff, 0x4b, 0x8f, 0x85, 0xbd, + 0xec, 0x69, 0x59, 0xda, 0xfd, 0x21, 0x4b, 0x27, 0x49, 0x97, 0xed, 0xb6, 0xa5, 0xa7, 0xbc, 0x79, + 0xe7, 0xf9, 0x9a, 0x27, 0x81, 0x2f, 0x07, 0x5c, 0x4e, 0xb9, 0x24, 0x82, 0xc6, 0x74, 0x2a, 0xc9, + 0x9f, 0x6e, 0xc0, 0x12, 0xda, 0x25, 0xb3, 0x94, 0xc5, 0x99, 0x27, 0x62, 0x9e, 0x70, 0xd4, 0xca, + 0x21, 0x5e, 0x0e, 0xf1, 0x0a, 0x88, 0xf5, 0x24, 0xe4, 0x21, 0x57, 0x08, 0xb2, 0x9e, 0x72, 0xb0, + 0xf5, 0x3c, 0xe4, 0x3c, 0x9c, 0x30, 0x42, 0xc5, 0x88, 0xd0, 0x28, 0xe2, 0x09, 0x4d, 0x46, 0x3c, + 0x92, 0xc5, 0x29, 0xde, 0xed, 0x56, 0x28, 0x2b, 0x0c, 0xee, 0x43, 0xf4, 0x6d, 0xed, 0xfe, 0x55, + 0x2d, 0x7d, 0x36, 0x4b, 0x99, 0x4c, 0x90, 0x05, 0x1b, 0x32, 0x0d, 0xa4, 0xa0, 0x03, 0x66, 0x02, + 0x07, 0xb8, 0x86, 0xbf, 0x79, 0x47, 0x8f, 0x60, 0x65, 0xcc, 0x32, 0x53, 0x57, 0xeb, 0xf5, 0x88, + 0x7f, 0xc0, 0xc7, 0xf7, 0x34, 0xa4, 0xe0, 0x91, 0x64, 0xe8, 0x13, 0xac, 0x29, 0x2b, 0xa5, 0xd0, + 0xec, 0x61, 0x6f, 0xe7, 0xcd, 0x3c, 0xc5, 0xfa, 0x3c, 0xa4, 0x51, 0xc8, 0xfa, 0xd5, 0xf9, 0x55, + 0x5b, 0xf3, 0x73, 0x1a, 0x7e, 0x06, 0x5b, 0x4a, 0xf6, 0x7b, 0xe1, 0x5c, 0xa6, 0xc3, 0x3f, 0xe1, + 0xd3, 0xed, 0x83, 0xc2, 0xf2, 0x23, 0x34, 0xca, 0x9c, 0xd2, 0x04, 0x4e, 0xc5, 0x6d, 0xf6, 0xda, + 0x7b, 0x6c, 0x4b, 0xb2, 0x7f, 0xc7, 0xc0, 0x1f, 0x60, 0xa3, 0x5c, 0x1f, 0xac, 0x00, 0xc1, 0xea, + 0x98, 0x65, 0xd2, 0xd4, 0x9d, 0x8a, 0x6b, 0xf8, 0x6a, 0xee, 0x9d, 0xeb, 0xb0, 0xa6, 0x52, 0xa1, + 0xff, 0x00, 0xd6, 0xf3, 0x2a, 0xd0, 0xeb, 0x3d, 0xe6, 0x0f, 0x2b, 0xb7, 0xde, 0x1c, 0x03, 0xcd, + 0xaf, 0x89, 0x5f, 0xfd, 0xbb, 0xb8, 0x39, 0xd3, 0xdb, 0xe8, 0x05, 0x39, 0xf4, 0x85, 0xd1, 0x29, + 0x80, 0xc6, 0xa6, 0x23, 0xf4, 0xf6, 0x90, 0xc1, 0x76, 0xc7, 0x56, 0xe7, 0x48, 0x74, 0x91, 0xc8, + 0x55, 0x89, 0x30, 0x72, 0xf6, 0x24, 0xda, 0x74, 0xdc, 0xff, 0x32, 0x5f, 0xda, 0x60, 0xb1, 0xb4, + 0xc1, 0xf5, 0xd2, 0x06, 0x27, 0x2b, 0x5b, 0x5b, 0xac, 0x6c, 0xed, 0x72, 0x65, 0x6b, 0xbf, 0xde, + 0x87, 0xa3, 0x64, 0x98, 0x06, 0xde, 0x80, 0x4f, 0x4b, 0x95, 0xfc, 0xd1, 0x91, 0xbf, 0xc7, 0xe4, + 0x6f, 0x29, 0x99, 0x64, 0x82, 0x49, 0x22, 0x62, 0x2e, 0xb8, 0xa4, 0x93, 0xa0, 0xae, 0xfe, 0xe3, + 0x77, 0xb7, 0x01, 0x00, 0x00, 0xff, 0xff, 0x69, 0xa9, 0xbc, 0x11, 0x5b, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -177,6 +323,8 @@ type QueryClient interface { // Params queries a specific parameter of a module, given its subspace and // key. Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) + // Subspaces queries for all registered subspaces and all keys for a subspace. + Subspaces(ctx context.Context, in *QuerySubspacesRequest, opts ...grpc.CallOption) (*QuerySubspacesResponse, error) } type queryClient struct { @@ -196,11 +344,22 @@ func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts . return out, nil } +func (c *queryClient) Subspaces(ctx context.Context, in *QuerySubspacesRequest, opts ...grpc.CallOption) (*QuerySubspacesResponse, error) { + out := new(QuerySubspacesResponse) + err := c.cc.Invoke(ctx, "/cosmos.params.v1beta1.Query/Subspaces", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Params queries a specific parameter of a module, given its subspace and // key. Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) + // Subspaces queries for all registered subspaces and all keys for a subspace. + Subspaces(context.Context, *QuerySubspacesRequest) (*QuerySubspacesResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -210,6 +369,9 @@ type UnimplementedQueryServer struct { func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") } +func (*UnimplementedQueryServer) Subspaces(ctx context.Context, req *QuerySubspacesRequest) (*QuerySubspacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Subspaces not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -233,6 +395,24 @@ func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interf return interceptor(ctx, in, info, handler) } +func _Query_Subspaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QuerySubspacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Subspaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cosmos.params.v1beta1.Query/Subspaces", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Subspaces(ctx, req.(*QuerySubspacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "cosmos.params.v1beta1.Query", HandlerType: (*QueryServer)(nil), @@ -241,6 +421,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "Params", Handler: _Query_Params_Handler, }, + { + MethodName: "Subspaces", + Handler: _Query_Subspaces_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "cosmos/params/v1beta1/query.proto", @@ -316,6 +500,105 @@ func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *QuerySubspacesRequest) 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 *QuerySubspacesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySubspacesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QuerySubspacesResponse) 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 *QuerySubspacesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QuerySubspacesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Subspaces) > 0 { + for iNdEx := len(m.Subspaces) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Subspaces[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Subspace) 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 *Subspace) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Subspace) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Keys) > 0 { + for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Keys[iNdEx]) + copy(dAtA[i:], m.Keys[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Keys[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Subspace) > 0 { + i -= len(m.Subspace) + copy(dAtA[i:], m.Subspace) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Subspace))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -355,6 +638,49 @@ func (m *QueryParamsResponse) Size() (n int) { return n } +func (m *QuerySubspacesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QuerySubspacesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Subspaces) > 0 { + for _, e := range m.Subspaces { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + +func (m *Subspace) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Subspace) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if len(m.Keys) > 0 { + for _, s := range m.Keys { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -558,6 +884,254 @@ func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QuerySubspacesRequest) 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 ErrIntOverflowQuery + } + 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: QuerySubspacesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySubspacesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QuerySubspacesResponse) 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 ErrIntOverflowQuery + } + 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: QuerySubspacesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QuerySubspacesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subspaces", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Subspaces = append(m.Subspaces, &Subspace{}) + if err := m.Subspaces[len(m.Subspaces)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Subspace) 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 ErrIntOverflowQuery + } + 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: Subspace: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Subspace: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subspace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Subspace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + 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 ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Keys = append(m.Keys, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/x/params/types/proposal/query.pb.gw.go b/x/params/types/proposal/query.pb.gw.go index f5b9f9d5b686..dcb9c9443c0c 100644 --- a/x/params/types/proposal/query.pb.gw.go +++ b/x/params/types/proposal/query.pb.gw.go @@ -67,6 +67,24 @@ func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshal } +func request_Query_Subspaces_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySubspacesRequest + var metadata runtime.ServerMetadata + + msg, err := client.Subspaces(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Subspaces_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QuerySubspacesRequest + var metadata runtime.ServerMetadata + + msg, err := server.Subspaces(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -93,6 +111,26 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_Subspaces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Subspaces_0(rctx, inboundMarshaler, server, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Subspaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -154,13 +192,37 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_Subspaces_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Subspaces_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Subspaces_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } var ( pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 1}, []string{"cosmos", "params", "v1beta1"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_Subspaces_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"cosmos", "params", "v1beta1", "subspaces"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( forward_Query_Params_0 = runtime.ForwardResponseMessage + + forward_Query_Subspaces_0 = runtime.ForwardResponseMessage ) diff --git a/x/params/types/subspace.go b/x/params/types/subspace.go index 42afe6cc9b21..541277dcf612 100644 --- a/x/params/types/subspace.go +++ b/x/params/types/subspace.go @@ -127,6 +127,22 @@ func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) { } } +// IterateKeys iterates over all the keys in the subspace and executes the +// provided callback. If the callback returns true for a given key, iteration +// will halt. +func (s Subspace) IterateKeys(ctx sdk.Context, cb func(key []byte) bool) { + store := s.kvStore(ctx) + + iter := sdk.KVStorePrefixIterator(store, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + if cb(iter.Key()) { + break + } + } +} + // GetRaw queries for the raw values bytes for a parameter by key. func (s Subspace) GetRaw(ctx sdk.Context, key []byte) []byte { store := s.kvStore(ctx) diff --git a/x/params/types/subspace_test.go b/x/params/types/subspace_test.go index 664c70df1801..d09e9c26b532 100644 --- a/x/params/types/subspace_test.go +++ b/x/params/types/subspace_test.go @@ -1,6 +1,7 @@ package types_test import ( + "bytes" "fmt" "testing" "time" @@ -92,6 +93,41 @@ func (suite *SubspaceTestSuite) TestGetRaw() { }) } +func (suite *SubspaceTestSuite) TestIterateKeys() { + suite.Require().NotPanics(func() { + suite.ss.Set(suite.ctx, keyUnbondingTime, time.Second) + }) + suite.Require().NotPanics(func() { + suite.ss.Set(suite.ctx, keyMaxValidators, uint16(50)) + }) + suite.Require().NotPanics(func() { + suite.ss.Set(suite.ctx, keyBondDenom, "stake") + }) + + var keys [][]byte + suite.ss.IterateKeys(suite.ctx, func(key []byte) bool { + keys = append(keys, key) + return false + }) + suite.Require().Len(keys, 3) + suite.Require().Contains(keys, keyUnbondingTime) + suite.Require().Contains(keys, keyMaxValidators) + suite.Require().Contains(keys, keyBondDenom) + + var keys2 [][]byte + suite.ss.IterateKeys(suite.ctx, func(key []byte) bool { + if bytes.Equal(key, keyUnbondingTime) { + return true + } + + keys2 = append(keys2, key) + return false + }) + suite.Require().Len(keys2, 2) + suite.Require().Contains(keys2, keyMaxValidators) + suite.Require().Contains(keys2, keyBondDenom) +} + func (suite *SubspaceTestSuite) TestHas() { t := time.Hour * 48 From 1e33f39cba46f189ef44c9fe26f3f88a78a55780 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Wed, 11 Aug 2021 08:47:07 +0200 Subject: [PATCH 29/31] chore: Update changelog with 0.42.8, 0.42.9 and 0.43.0 (#9891) ## Description ref: #9116 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 62 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11184090ae53..82101c103392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9776](https://github.com/cosmos/cosmos-sdk/pull/9776) Add flag `staking-bond-denom` to specify the staking bond denomination value when initializing a new chain. * [\#9533](https://github.com/cosmos/cosmos-sdk/pull/9533) Added a new gRPC method, `DenomOwners`, in `x/bank` to query for all account holders of a specific denomination. * (bank) [\#9618](https://github.com/cosmos/cosmos-sdk/pull/9618) Update bank.Metadata: add URI and URIHash attributes. -* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.acc_seq='/'`). * [\#9837](https://github.com/cosmos/cosmos-sdk/issues/9837) `--generate-only` flag will accept the keyname now. ### API Breaking Changes @@ -66,42 +65,29 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9432](https://github.com/cosmos/cosmos-sdk/pull/9432) `ConsensusParamsKeyTable` moved from `params/keeper` to `params/types` * [\#9576](https://github.com/cosmos/cosmos-sdk/pull/9576) Add debug error message to `sdkerrors.QueryResult` when enabled * [\#9650](https://github.com/cosmos/cosmos-sdk/pull/9650) Removed deprecated message handler implementation from the SDK modules. -* (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. App must add x/capability to begin blockers which will assure that the x/capability keeper is properly initialized. The x/capability begin blocker must be run before any other module which uses x/capability. * (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) `AddressFromBalancesStore` renamed to `AddressAndDenomFromBalancesStore`. ### Client Breaking Changes -* [\#9859](https://github.com/cosmos/cosmos-sdk/pull/9859) The `default` pruning strategy now keeps the last 362880 blocks instead of 100. 362880 equates to roughly enough blocks to cover the entire unbonding period assuming a 21 day unbonding period and 5s block time. * [\#9594](https://github.com/cosmos/cosmos-sdk/pull/9594) Remove legacy REST API. Please see the [REST Endpoints Migration guide](https://docs.cosmos.network/master/migrations/rest.html) to migrate to the new REST endpoints. -* [\#9781](https://github.com/cosmos/cosmos-sdk/pull/9781) Improve`withdraw-all-rewards` UX when broadcast mode `async` or `async` is used. -* [\#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) Missing coin denomination in logs ### CLI Breaking Changes -* [\#9827](https://github.com/cosmos/cosmos-sdk/pull/9827) Ensure input parity of validator public key input between `tx staking create-validator` and `gentx`. * [\#9246](https://github.com/cosmos/cosmos-sdk/pull/9246) Removed the CLI flag `--setup-config-only` from the `testnet` command and added the subcommand `init-files`. -* [\#9371](https://github.com/cosmos/cosmos-sdk/pull/9371) Non-zero default fees/Server will error if there's an empty value for min-gas-price in app.toml ### Improvements * (cli) [\#9856](https://github.com/cosmos/cosmos-sdk/pull/9856) Overwrite `--sequence` and `--account-number` flags with default flag values when used with `offline=false` in `sign-batch` command. -* (cli) [\#9593](https://github.com/cosmos/cosmos-sdk/pull/9593) Check if chain-id is blank before verifying signatures in multisign and error. -* (cli) [\#9717](https://github.com/cosmos/cosmos-sdk/pull/9717) Added CLI flag `--output json/text` to `tx` cli commands. ### Bug Fixes -* [\#9766](https://github.com/cosmos/cosmos-sdk/pull/9766) Fix hardcoded ledger signing algorithm on `keys add` command. -* [\#9720](https://github.com/cosmos/cosmos-sdk/pull/9720) Feegrant grant cli granter now accepts key name as well as address in general and accepts only address in --generate-only mode * [\#9651](https://github.com/cosmos/cosmos-sdk/pull/9651) Change inconsistent limit of `0` to `MaxUint64` on InfiniteGasMeter and add GasRemaining func to GasMeter. * [\#9639](https://github.com/cosmos/cosmos-sdk/pull/9639) Check store keys length before accessing them by making sure that `key` is of length `m+1` (for `key[n:m]`) * (types) [\#9627](https://github.com/cosmos/cosmos-sdk/pull/9627) Fix nil pointer panic on `NewBigIntFromInt` * (x/genutil) [\#9574](https://github.com/cosmos/cosmos-sdk/pull/9575) Actually use the `gentx` client tx flags (like `--keyring-dir`) * (x/distribution) [\#9599](https://github.com/cosmos/cosmos-sdk/pull/9599) Withdraw rewards event now includes a value attribute even if there are 0 rewards (due to situations like 100% commission). * (x/genutil) [\#9638](https://github.com/cosmos/cosmos-sdk/pull/9638) Added missing validator key save when recovering from mnemonic -* (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. * [\#9762](https://github.com/cosmos/cosmos-sdk/pull/9762) The init command uses the chain-id from the client config if --chain-id is not provided -* [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. -* [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Fixes capability initialization issue on tx revert by moving initialization logic to `InitChain` and `BeginBlock`. * [\#9854](https://github.com/cosmos/cosmos-sdk/pull/9854) Fixed the `make proto-gen` to get dynamic container name based on project name for the cosmos based sdks. * [\#9829](https://github.com/cosmos/cosmos-sdk/pull/9829) Fixed Coin denom sorting not being checked during `Balance.Validate` check. Refactored the Validation logic to use `Coins.Validate` for `Balance.Coins`. @@ -111,7 +97,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ token holders of a specific denomination. `DenomOwners` is updated to use the new reverse index. * (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) Account balance is stored as `sdk.Int` rather than `sdk.Coin`. -## [v0.43.0-rc0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.43.0-rc0) - 2021-06-25 +## [v0.43.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.43.0) - 2021-08-10 ### Features @@ -149,6 +135,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * via `ServiceMsg` TypeURLs (e.g. `message.action='/cosmos.bank.v1beta1.Msg/Send'`) does not work anymore, * via legacy `msg.Type()` (e.g. `message.action='send'`) is being deprecated, new `Msg`s won't emit these events. * Please use concrete `Msg` TypeURLs instead (e.g. `message.action='/cosmos.bank.v1beta1.MsgSend'`). +* [\#9859](https://github.com/cosmos/cosmos-sdk/pull/9859) The `default` pruning strategy now keeps the last 362880 blocks instead of 100. 362880 equates to roughly enough blocks to cover the entire unbonding period assuming a 21 day unbonding period and 5s block time. +* [\#9785](https://github.com/cosmos/cosmos-sdk/issues/9785) Missing coin denomination in logs ### API Breaking Changes @@ -194,7 +182,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [\#9271](https://github.com/cosmos/cosmos-sdk/pull/9271) SendEnabledCoin(s) renamed to IsSendEnabledCoin(s) to better reflect its functionality. * (x/bank) [\#9550](https://github.com/cosmos/cosmos-sdk/pull/9550) `server.InterceptConfigsPreRunHandler` now takes 2 additional arguments: customAppConfigTemplate and customAppConfig. If you don't need to customize these, simply put `""` and `nil`. * [\#8245](https://github.com/cosmos/cosmos-sdk/pull/8245) Removed `simapp.MakeCodecs` and use `simapp.MakeTestEncodingConfig` instead. - +* (x/capability) [\#9836](https://github.com/cosmos/cosmos-sdk/pull/9836) Removed `InitializeAndSeal(ctx sdk.Context)` and replaced with `Seal()`. App must add x/capability module to the begin blockers which will assure that the x/capability keeper is properly initialized. The x/capability begin blocker must be run before any other module which uses x/capability. ### State Machine Breaking @@ -212,7 +200,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#8628](https://github.com/cosmos/cosmos-sdk/issues/8628) Commands no longer print outputs using `stderr` by default * [\#9134](https://github.com/cosmos/cosmos-sdk/pull/9134) Renamed the CLI flag `--memo` to `--note`. * [\#9291](https://github.com/cosmos/cosmos-sdk/pull/9291) Migration scripts prior to v0.38 have been removed from the CLI `migrate` command. The oldest supported migration is v0.39->v0.42. - +* [\#9371](https://github.com/cosmos/cosmos-sdk/pull/9371) Non-zero default fees/Server will error if there's an empty value for min-gas-price in app.toml +* [\#9827](https://github.com/cosmos/cosmos-sdk/pull/9827) Ensure input parity of validator public key input between `tx staking create-validator` and `gentx`. +* [\#9621](https://github.com/cosmos/cosmos-sdk/pull/9621) Rollback [\#9371](https://github.com/cosmos/cosmos-sdk/pull/9371) and log warning if there's an empty value for min-gas-price in app.toml ### Improvements @@ -227,7 +217,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (keyring) [\#8826](https://github.com/cosmos/cosmos-sdk/pull/8826) add trust to macOS Keychain for calling apps by default, avoiding repeating keychain popups that appears when dealing with keyring (key add, list, ...) operations. * (makefile) [\#7933](https://github.com/cosmos/cosmos-sdk/issues/7933) Use Docker to generate swagger files. * (crypto/types) [\#9196](https://github.com/cosmos/cosmos-sdk/pull/9196) Fix negative index accesses in CompactUnmarshal,GetIndex,SetIndex -* (cli) [\#9201](https://github.com/cosmos/cosmos-sdk/pull/9201) Fix init --recover not working. * (makefile) [\#9192](https://github.com/cosmos/cosmos-sdk/pull/9192) Reuse proto containers in proto related jobs. * [\#9205](https://github.com/cosmos/cosmos-sdk/pull/9205) Improve readability in `abci` handleQueryP2P * [\#9231](https://github.com/cosmos/cosmos-sdk/pull/9231) Remove redundant staking errors. @@ -244,7 +233,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes -* (x/gov) [\#8813](https://github.com/cosmos/cosmos-sdk/pull/8813) fix `{appd} q gov deposits [proposal-id]`, `GET /gov/proposals/{proposal_id}/deposits` to include initial deposit. * (gRPC) [\#8945](https://github.com/cosmos/cosmos-sdk/pull/8945) gRPC reflection now works correctly. * (keyring) [#\8635](https://github.com/cosmos/cosmos-sdk/issues/8635) Remove hardcoded default passphrase value on `NewMnemonic` * (x/bank) [\#8434](https://github.com/cosmos/cosmos-sdk/pull/8434) Fix legacy REST API `GET /bank/total` and `GET /bank/total/{denom}` in swagger @@ -256,6 +244,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#9480](https://github.com/cosmos/cosmos-sdk/pull/9480) Fix added keys when using `--dry-run`. * (types) [\#9511](https://github.com/cosmos/cosmos-sdk/pull/9511) Change `maxBitLen` of `sdk.Int` and `sdk.Dec` to handle max ERC20 value. * [\#9454](https://github.com/cosmos/cosmos-sdk/pull/9454) Fix testnet command with --node-dir-prefix accepts `-` and change `node-dir-prefix token` to `testtoken`. +* (keyring) [\#9562](https://github.com/cosmos/cosmos-sdk/pull/9563) fix keyring kwallet backend when using with empty wallet. * (keyring) [\#9583](https://github.com/cosmos/cosmos-sdk/pull/9583) Fix correct population of legacy `Vote.Option` field for votes with 1 VoteOption of weight 1. * (x/distinction) [\#8918](https://github.com/cosmos/cosmos-sdk/pull/8918) Fix module's parameters validation. * (x/gov/types) [\#8586](https://github.com/cosmos/cosmos-sdk/pull/8586) Fix bug caused by NewProposal that unnecessarily creates a Proposal object that’s discarded on any error. @@ -264,6 +253,11 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [\#8531](https://github.com/cosmos/cosmos-sdk/pull/8531) Fix bug caused by ignoring errors returned by Balance.GetAddress() * (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value * [\#8282](https://github.com/cosmos/cosmos-sdk/pull/8282) fix zero time checks +* (cli) [\#9593](https://github.com/cosmos/cosmos-sdk/pull/9593) Check if chain-id is blank before verifying signatures in multisign and error. +* [\#9720](https://github.com/cosmos/cosmos-sdk/pull/9720) Feegrant grant cli granter now accepts key name as well as address in general and accepts only address in --generate-only mode +* [\#9793](https://github.com/cosmos/cosmos-sdk/pull/9793) Fixed ECDSA/secp256r1 transaction malleability. +* (server) [#9704](https://github.com/cosmos/cosmos-sdk/pull/9704) Start GRPCWebServer in goroutine, avoid blocking other services from starting. +* (bank) [\#9687](https://github.com/cosmos/cosmos-sdk/issues/9687) fixes [\#9159](https://github.com/cosmos/cosmos-sdk/issues/9159). Added migration to prune balances with zero coins. ### Deprecated @@ -271,6 +265,36 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (grpc) [\#8926](https://github.com/cosmos/cosmos-sdk/pull/8926) The `tx` field in `SimulateRequest` has been deprecated, prefer to pass `tx_bytes` instead. * (sdk types) [\#9498](https://github.com/cosmos/cosmos-sdk/pull/9498) `clientContext.JSONCodec` will be removed in the next version. use `clientContext.Codec` instead. +## [v0.42.9](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.9) - 2021-08-04 + +### Bug Fixes + +* [\#9835](https://github.com/cosmos/cosmos-sdk/pull/9835) Moved capability initialization logic to BeginBlocker to fix nondeterminsim issue mentioned in [\#9800](https://github.com/cosmos/cosmos-sdk/issues/9800). Applications must now include the capability module in their BeginBlocker order before any module that uses capabilities gets run. +* [\#9201](https://github.com/cosmos/cosmos-sdk/pull/9201) Fixed ` init --recover` flag. + + +### API Breaking Changes + +* [\#9835](https://github.com/cosmos/cosmos-sdk/pull/9835) The `InitializeAndSeal` API has not changed, however it no longer initializes the in-memory state. `InitMemStore` has been introduced to serve this function, which will be called either in `InitChain` or `BeginBlock` (whichever is first after app start). Nodes may run this version on a network running 0.42.x, however, they must update their app.go files to include the capability module in their begin blockers. + +### Client Breaking Changes + +* [\#9781](https://github.com/cosmos/cosmos-sdk/pull/9781) Improve`withdraw-all-rewards` UX when broadcast mode `async` or `async` is used. + +## [v0.42.8](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.8) - 2021-07-30 + +### Features + +* [\#9750](https://github.com/cosmos/cosmos-sdk/pull/9750) Emit events for tx signature and sequence, so clients can now query txs by signature (`tx.signature=''`) or by address and sequence combo (`tx.acc_seq='/'`). + +### Improvements + +* (cli) [\#9717](https://github.com/cosmos/cosmos-sdk/pull/9717) Added CLI flag `--output json/text` to `tx` cli commands. + +### Bug Fixes + +* [\#9766](https://github.com/cosmos/cosmos-sdk/pull/9766) Fix hardcoded ledger signing algorithm on `keys add` command. + ## [v0.42.7](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.42.7) - 2021-07-09 ### Improvements From c45b9d0da6a07e445e60e0ef62d18b8a0da2ff1e Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 11 Aug 2021 16:13:17 +0700 Subject: [PATCH 30/31] test: fix BenchmarkCoinsAdditionIntersect panic (#9904) --- types/coin_benchmark_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/coin_benchmark_test.go b/types/coin_benchmark_test.go index 8c8088923e4b..9730284c9b3d 100644 --- a/types/coin_benchmark_test.go +++ b/types/coin_benchmark_test.go @@ -6,7 +6,7 @@ import ( ) func coinName(suffix int) string { - return fmt.Sprintf("coinz%d", suffix) + return fmt.Sprintf("coinz%04d", suffix) } func BenchmarkCoinsAdditionIntersect(b *testing.B) { From c14b101a7a3ea6b126261a9e2d7b9a937140ceba Mon Sep 17 00:00:00 2001 From: MD Aleem <72057206+aleem1314@users.noreply.github.com> Date: Wed, 11 Aug 2021 15:26:51 +0530 Subject: [PATCH 31/31] fix!: remove denom from DenomMetadata key (#9890) ## Description Closes: #9643 --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- CHANGELOG.md | 1 + x/bank/keeper/keeper.go | 4 +- x/bank/migrations/v043/keys.go | 11 ++++- x/bank/migrations/v044/store.go | 26 +++++++++- x/bank/migrations/v044/store_test.go | 74 ++++++++++++++++++++++++++++ x/bank/types/key.go | 6 --- 6 files changed, 111 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82101c103392..36b268013073 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (x/bank) [\#9611](https://github.com/cosmos/cosmos-sdk/pull/9611) Introduce a new index to act as a reverse index between a denomination and address allowing to query for token holders of a specific denomination. `DenomOwners` is updated to use the new reverse index. * (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) Account balance is stored as `sdk.Int` rather than `sdk.Coin`. +* (x/bank) [\#9890] (https://github.com/cosmos/cosmos-sdk/pull/9890) Remove duplicate denom from denom metadata key. ## [v0.43.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.43.0) - 2021-08-10 diff --git a/x/bank/keeper/keeper.go b/x/bank/keeper/keeper.go index cc8ded240136..150d200cf0ad 100644 --- a/x/bank/keeper/keeper.go +++ b/x/bank/keeper/keeper.go @@ -218,7 +218,7 @@ func (k BaseKeeper) GetSupply(ctx sdk.Context, denom string) sdk.Coin { // false otherwise. func (k BaseKeeper) GetDenomMetaData(ctx sdk.Context, denom string) (types.Metadata, bool) { store := ctx.KVStore(k.storeKey) - store = prefix.NewStore(store, types.DenomMetadataKey(denom)) + store = prefix.NewStore(store, types.DenomMetadataPrefix) bz := store.Get([]byte(denom)) if bz == nil { @@ -265,7 +265,7 @@ func (k BaseKeeper) IterateAllDenomMetaData(ctx sdk.Context, cb func(types.Metad // 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)) + denomMetaDataStore := prefix.NewStore(store, types.DenomMetadataPrefix) m := k.cdc.MustMarshal(&denomMetaData) denomMetaDataStore.Set([]byte(denomMetaData.Base), m) diff --git a/x/bank/migrations/v043/keys.go b/x/bank/migrations/v043/keys.go index 01a4aab2ab51..ef24217ef0f3 100644 --- a/x/bank/migrations/v043/keys.go +++ b/x/bank/migrations/v043/keys.go @@ -12,8 +12,9 @@ const ( ) var ( - SupplyKey = []byte{0x00} - BalancesPrefix = []byte{0x02} + SupplyKey = []byte{0x00} + BalancesPrefix = []byte{0x02} + DenomMetadataPrefix = []byte{0x1} ErrInvalidKey = errors.New("invalid key") ) @@ -36,3 +37,9 @@ func AddressFromBalancesStore(key []byte) (sdk.AccAddress, error) { return key[1 : bound+1], nil } + +// DenomMetadataKey returns the denomination metadata key. +func DenomMetadataKey(denom string) []byte { + d := []byte(denom) + return append(DenomMetadataPrefix, d...) +} diff --git a/x/bank/migrations/v044/store.go b/x/bank/migrations/v044/store.go index faaa07a9068a..b43404573121 100644 --- a/x/bank/migrations/v044/store.go +++ b/x/bank/migrations/v044/store.go @@ -14,9 +14,15 @@ import ( // // - Migrate coin storage to save only amount. // - Add an additional reverse index from denomination to address. +// - Remove duplicate denom from denom metadata store key. func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { store := ctx.KVStore(storeKey) - return addDenomReverseIndex(store, cdc) + err := addDenomReverseIndex(store, cdc) + if err != nil { + return err + } + + return migrateDenomMetadata(store) } func addDenomReverseIndex(store sdk.KVStore, cdc codec.BinaryCodec) error { @@ -64,3 +70,21 @@ func addDenomReverseIndex(store sdk.KVStore, cdc codec.BinaryCodec) error { return nil } + +func migrateDenomMetadata(store sdk.KVStore) error { + oldDenomMetaDataStore := prefix.NewStore(store, v043.DenomMetadataPrefix) + + oldDenomMetaDataIter := oldDenomMetaDataStore.Iterator(nil, nil) + defer oldDenomMetaDataIter.Close() + + for ; oldDenomMetaDataIter.Valid(); oldDenomMetaDataIter.Next() { + oldKey := oldDenomMetaDataIter.Key() + // old key: prefix_bytes | denom_bytes | denom_bytes + newKey := append(types.DenomMetadataPrefix, oldKey[:len(oldKey)/2+1]...) + + store.Set(newKey, oldDenomMetaDataIter.Value()) + oldDenomMetaDataStore.Delete(oldDenomMetaDataIter.Key()) + } + + return nil +} diff --git a/x/bank/migrations/v044/store_test.go b/x/bank/migrations/v044/store_test.go index 61028e389440..37c268d3d702 100644 --- a/x/bank/migrations/v044/store_test.go +++ b/x/bank/migrations/v044/store_test.go @@ -52,3 +52,77 @@ func TestMigrateStore(t *testing.T) { require.NotNil(t, bz) } } + +func TestMigrateDenomMetaData(t *testing.T) { + encCfg := simapp.MakeTestEncodingConfig() + bankKey := sdk.NewKVStoreKey("bank") + ctx := testutil.DefaultContext(bankKey, sdk.NewTransientStoreKey("transient_test")) + store := ctx.KVStore(bankKey) + metaData := []types.Metadata{ + { + Name: "Cosmos Hub Atom", + Symbol: "ATOM", + Description: "The native staking token of the Cosmos Hub.", + DenomUnits: []*types.DenomUnit{ + {"uatom", uint32(0), []string{"microatom"}}, + {"matom", uint32(3), []string{"milliatom"}}, + {"atom", uint32(6), nil}, + }, + Base: "uatom", + Display: "atom", + }, + { + Name: "Token", + Symbol: "TOKEN", + Description: "The native staking token of the Token Hub.", + DenomUnits: []*types.DenomUnit{ + {"1token", uint32(5), []string{"decitoken"}}, + {"2token", uint32(4), []string{"centitoken"}}, + {"3token", uint32(7), []string{"dekatoken"}}, + }, + Base: "utoken", + Display: "token", + }, + } + denomMetadataStore := prefix.NewStore(store, v043.DenomMetadataPrefix) + + for i := range []int{0, 1} { + key := append(v043.DenomMetadataPrefix, []byte(metaData[i].Base)...) + key = append(key, []byte(metaData[i].Base)...) + bz, err := encCfg.Codec.Marshal(&metaData[i]) + require.NoError(t, err) + denomMetadataStore.Set(key, bz) + } + + require.NoError(t, v044.MigrateStore(ctx, bankKey, encCfg.Codec)) + + denomMetadataStore = prefix.NewStore(store, v043.DenomMetadataPrefix) + denomMetadataIter := denomMetadataStore.Iterator(nil, nil) + defer denomMetadataIter.Close() + for i := 0; denomMetadataIter.Valid(); denomMetadataIter.Next() { + var result types.Metadata + newKey := denomMetadataIter.Key() + + // make sure old entry is deleted + oldKey := append(newKey, newKey[1:]...) + bz := denomMetadataStore.Get(oldKey) + require.Nil(t, bz) + + require.Equal(t, string(newKey)[1:], metaData[i].Base) + bz = denomMetadataStore.Get(denomMetadataIter.Key()) + require.NotNil(t, bz) + err := encCfg.Codec.Unmarshal(bz, &result) + require.NoError(t, err) + assertMetaDataEqual(t, metaData[i], result) + i++ + } +} + +func assertMetaDataEqual(t *testing.T, expected, actual types.Metadata) { + require.Equal(t, expected.GetBase(), actual.GetBase()) + require.Equal(t, expected.GetDisplay(), actual.GetDisplay()) + require.Equal(t, expected.GetDescription(), actual.GetDescription()) + require.Equal(t, expected.GetDenomUnits()[1].GetDenom(), actual.GetDenomUnits()[1].GetDenom()) + require.Equal(t, expected.GetDenomUnits()[1].GetExponent(), actual.GetDenomUnits()[1].GetExponent()) + require.Equal(t, expected.GetDenomUnits()[1].GetAliases(), actual.GetDenomUnits()[1].GetAliases()) +} diff --git a/x/bank/types/key.go b/x/bank/types/key.go index e3b6aa87afa3..dd01a4d49065 100644 --- a/x/bank/types/key.go +++ b/x/bank/types/key.go @@ -31,12 +31,6 @@ var ( BalancesPrefix = []byte{0x02} ) -// DenomMetadataKey returns the denomination metadata key. -func DenomMetadataKey(denom string) []byte { - d := []byte(denom) - return append(DenomMetadataPrefix, d...) -} - // 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.