Skip to content

Commit 1925123

Browse files
RiccardoMamaury1093fedekunzemergify[bot]
authored and
zakir
committed
auth: query all accounts stored via gRPC (cosmos#8522)
* Added the ability to query all accounts stored using gRPC * Added CHANGELOG entry * Fixed linting errors * Update CHANGELOG.md Co-authored-by: Amaury <[email protected]> * Update proto/cosmos/auth/v1beta1/query.proto Co-authored-by: Amaury <[email protected]> * Run make proto-all * Merged origin/master * Applied suggestions * Added CLI command * Updated CHANGELOG * Fixed merge conflicts Co-authored-by: Amaury <[email protected]> Co-authored-by: Federico Kunze <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 842d5b2 commit 1925123

File tree

7 files changed

+751
-57
lines changed

7 files changed

+751
-57
lines changed

proto/cosmos/auth/v1beta1/query.proto

+21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
syntax = "proto3";
22
package cosmos.auth.v1beta1;
33

4+
import "cosmos/base/query/v1beta1/pagination.proto";
45
import "gogoproto/gogo.proto";
56
import "google/protobuf/any.proto";
67
import "google/api/annotations.proto";
@@ -11,6 +12,11 @@ option go_package = "github.com/cosmos/cosmos-sdk/x/auth/types";
1112

1213
// Query defines the gRPC querier service.
1314
service Query {
15+
// Accounts returns all the existing accounts
16+
rpc Accounts(QueryAccountsRequest) returns (QueryAccountsResponse) {
17+
option (google.api.http).get = "/cosmos/auth/v1beta1/accounts";
18+
}
19+
1420
// Account returns account details based on address.
1521
rpc Account(QueryAccountRequest) returns (QueryAccountResponse) {
1622
option (google.api.http).get = "/cosmos/auth/v1beta1/accounts/{address}";
@@ -22,6 +28,21 @@ service Query {
2228
}
2329
}
2430

31+
// QueryAccountsRequest is the request type for the Query/Accounts RPC method.
32+
message QueryAccountsRequest {
33+
// pagination defines an optional pagination for the request.
34+
cosmos.base.query.v1beta1.PageRequest pagination = 1;
35+
}
36+
37+
// QueryAccountsResponse is the response type for the Query/Accounts RPC method.
38+
message QueryAccountsResponse {
39+
// accounts are the existing accounts
40+
repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "AccountI"];
41+
42+
// pagination defines the pagination in the response.
43+
cosmos.base.query.v1beta1.PageResponse pagination = 2;
44+
}
45+
2546
// QueryAccountRequest is the request type for the Query/Account RPC method.
2647
message QueryAccountRequest {
2748
option (gogoproto.equal) = false;

proto/cosmos/staking/v1beta1/staking.proto

+25-25
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ message CommissionRates {
2828
option (gogoproto.goproto_stringer) = false;
2929

3030
// rate is the commission rate charged to delegators, as a fraction.
31-
string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
31+
string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
3232
// max_rate defines the maximum commission rate which validator can ever charge, as a fraction.
3333
string max_rate = 2 [
3434
(gogoproto.moretags) = "yaml:\"max_rate\"",
@@ -49,9 +49,9 @@ message Commission {
4949
option (gogoproto.goproto_stringer) = false;
5050

5151
// commission_rates defines the initial commission rates to be used for creating a validator.
52-
CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
52+
CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
5353
// update_time is the last time the commission rate was changed.
54-
google.protobuf.Timestamp update_time = 2
54+
google.protobuf.Timestamp update_time = 2
5555
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""];
5656
}
5757

@@ -61,15 +61,15 @@ message Description {
6161
option (gogoproto.goproto_stringer) = false;
6262

6363
// moniker defines a human-readable name for the validator.
64-
string moniker = 1;
64+
string moniker = 1;
6565
// identity defines an optional identity signature (ex. UPort or Keybase).
66-
string identity = 2;
66+
string identity = 2;
6767
// website defines an optional website link.
68-
string website = 3;
68+
string website = 3;
6969
// security_contact defines an optional email for security contact.
7070
string security_contact = 4 [(gogoproto.moretags) = "yaml:\"security_contact\""];
7171
// details define other optional details.
72-
string details = 5;
72+
string details = 5;
7373
}
7474

7575
// Validator defines a validator, together with the total amount of the
@@ -86,12 +86,12 @@ message Validator {
8686
option (gogoproto.goproto_getters) = false;
8787

8888
// operator_address defines the address of the validator's operator; bech encoded in JSON.
89-
string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""];
89+
string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""];
9090
// consensus_pubkey is the consensus public key of the validator, as a Protobuf Any.
9191
google.protobuf.Any consensus_pubkey = 2
9292
[(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", (gogoproto.moretags) = "yaml:\"consensus_pubkey\""];
9393
// jailed defined whether the validator has been jailed from bonded status or not.
94-
bool jailed = 3;
94+
bool jailed = 3;
9595
// status is the validator status (bonded/unbonding/unbonded).
9696
BondStatus status = 4;
9797
// tokens define the delegated tokens (incl. self-delegation).
@@ -103,16 +103,16 @@ message Validator {
103103
(gogoproto.nullable) = false
104104
];
105105
// description defines the description terms for the validator.
106-
Description description = 7 [(gogoproto.nullable) = false];
106+
Description description = 7 [(gogoproto.nullable) = false];
107107
// unbonding_height defines, if unbonding, the height at which this validator has begun unbonding.
108-
int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""];
108+
int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""];
109109
// unbonding_time defines, if unbonding, the min time for the validator to complete unbonding.
110-
google.protobuf.Timestamp unbonding_time = 9
110+
google.protobuf.Timestamp unbonding_time = 9
111111
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
112112
// commission defines the commission parameters.
113-
Commission commission = 10 [(gogoproto.nullable) = false];
113+
Commission commission = 10 [(gogoproto.nullable) = false];
114114
// min_self_delegation is the validator's self declared minimum self delegation.
115-
string min_self_delegation = 11 [
115+
string min_self_delegation = 11 [
116116
(gogoproto.moretags) = "yaml:\"min_self_delegation\"",
117117
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
118118
(gogoproto.nullable) = false
@@ -201,9 +201,9 @@ message UnbondingDelegation {
201201
option (gogoproto.goproto_stringer) = false;
202202

203203
// delegator_address is the bech32-encoded address of the delegator.
204-
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
204+
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
205205
// validator_address is the bech32-encoded address of the validator.
206-
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
206+
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
207207
// entries are the unbonding delegation entries.
208208
repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries
209209
}
@@ -214,7 +214,7 @@ message UnbondingDelegationEntry {
214214
option (gogoproto.goproto_stringer) = false;
215215

216216
// creation_height is the height which the unbonding took place.
217-
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
217+
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
218218
// completion_time is the unix time for unbonding completion.
219219
google.protobuf.Timestamp completion_time = 2
220220
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
@@ -234,7 +234,7 @@ message RedelegationEntry {
234234
option (gogoproto.goproto_stringer) = false;
235235

236236
// creation_height defines the height which the redelegation took place.
237-
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
237+
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
238238
// completion_time defines the unix time for redelegation completion.
239239
google.protobuf.Timestamp completion_time = 2
240240
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
@@ -257,13 +257,13 @@ message Redelegation {
257257
option (gogoproto.goproto_stringer) = false;
258258

259259
// delegator_address is the bech32-encoded address of the delegator.
260-
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
260+
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
261261
// validator_src_address is the validator redelegation source operator address.
262-
string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""];
262+
string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""];
263263
// validator_dst_address is the validator redelegation destination operator address.
264-
string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""];
264+
string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""];
265265
// entries are the redelegation entries.
266-
repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries
266+
repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries
267267
}
268268

269269
// Params defines the parameters for the staking module.
@@ -275,13 +275,13 @@ message Params {
275275
google.protobuf.Duration unbonding_time = 1
276276
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
277277
// max_validators is the maximum number of validators.
278-
uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""];
278+
uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""];
279279
// max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio).
280-
uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""];
280+
uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""];
281281
// historical_entries is the number of historical entries to persist.
282282
uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""];
283283
// bond_denom defines the bondable coin denomination.
284-
string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""];
284+
string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""];
285285
}
286286

287287
// DelegationResponse is equivalent to Delegation except that it contains a

x/auth/client/cli/query.go

+33
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func GetQueryCmd() *cobra.Command {
4242

4343
cmd.AddCommand(
4444
GetAccountCmd(),
45+
GetAccountsCmd(),
4546
QueryParamsCmd(),
4647
)
4748

@@ -111,6 +112,38 @@ func GetAccountCmd() *cobra.Command {
111112
return cmd
112113
}
113114

115+
// GetAccountsCmd returns a query command that will display a list of accounts
116+
func GetAccountsCmd() *cobra.Command {
117+
cmd := &cobra.Command{
118+
Use: "accounts",
119+
Short: "Query all the accounts",
120+
RunE: func(cmd *cobra.Command, args []string) error {
121+
clientCtx, err := client.GetClientQueryContext(cmd)
122+
if err != nil {
123+
return err
124+
}
125+
126+
pageReq, err := client.ReadPageRequest(cmd.Flags())
127+
if err != nil {
128+
return err
129+
}
130+
131+
queryClient := types.NewQueryClient(clientCtx)
132+
res, err := queryClient.Accounts(cmd.Context(), &types.QueryAccountsRequest{Pagination: pageReq})
133+
if err != nil {
134+
return err
135+
}
136+
137+
return clientCtx.PrintProto(res)
138+
},
139+
}
140+
141+
flags.AddQueryFlagsToCmd(cmd)
142+
flags.AddPaginationFlagsToCmd(cmd, "all-accounts")
143+
144+
return cmd
145+
}
146+
114147
// QueryTxsByEventsCmd returns a command to search through transactions by events.
115148
func QueryTxsByEventsCmd() *cobra.Command {
116149
cmd := &cobra.Command{

x/auth/keeper/grpc_query.go

+31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package keeper
33
import (
44
"context"
55

6+
"github.com/cosmos/cosmos-sdk/store/prefix"
7+
"github.com/cosmos/cosmos-sdk/types/query"
8+
69
"google.golang.org/grpc/codes"
710
"google.golang.org/grpc/status"
811

@@ -13,6 +16,34 @@ import (
1316

1417
var _ types.QueryServer = AccountKeeper{}
1518

19+
func (ak AccountKeeper) Accounts(c context.Context, req *types.QueryAccountsRequest) (*types.QueryAccountsResponse, error) {
20+
if req == nil {
21+
return nil, status.Error(codes.InvalidArgument, "empty request")
22+
}
23+
24+
ctx := sdk.UnwrapSDKContext(c)
25+
store := ctx.KVStore(ak.key)
26+
accountsStore := prefix.NewStore(store, types.AddressStoreKeyPrefix)
27+
28+
var accounts []*codectypes.Any
29+
pageRes, err := query.Paginate(accountsStore, req.Pagination, func(key, value []byte) error {
30+
account := ak.decodeAccount(value)
31+
any, err := codectypes.NewAnyWithValue(account)
32+
if err != nil {
33+
return err
34+
}
35+
36+
accounts = append(accounts, any)
37+
return nil
38+
})
39+
40+
if err != nil {
41+
return nil, status.Errorf(codes.Internal, "paginate: %v", err)
42+
}
43+
44+
return &types.QueryAccountsResponse{Accounts: accounts, Pagination: pageRes}, err
45+
}
46+
1647
// Account returns account details based on address
1748
func (ak AccountKeeper) Account(c context.Context, req *types.QueryAccountRequest) (*types.QueryAccountResponse, error) {
1849
if req == nil {

x/auth/keeper/grpc_query_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,64 @@ import (
88
"github.com/cosmos/cosmos-sdk/x/auth/types"
99
)
1010

11+
func (suite *KeeperTestSuite) TestGRPCQueryAccounts() {
12+
var (
13+
req *types.QueryAccountsRequest
14+
)
15+
_, _, first := testdata.KeyTestPubAddr()
16+
_, _, second := testdata.KeyTestPubAddr()
17+
18+
testCases := []struct {
19+
msg string
20+
malleate func()
21+
expPass bool
22+
posttests func(res *types.QueryAccountsResponse)
23+
}{
24+
{
25+
"success",
26+
func() {
27+
suite.app.AccountKeeper.SetAccount(suite.ctx,
28+
suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, first))
29+
suite.app.AccountKeeper.SetAccount(suite.ctx,
30+
suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, second))
31+
req = &types.QueryAccountsRequest{}
32+
},
33+
true,
34+
func(res *types.QueryAccountsResponse) {
35+
for _, acc := range res.Accounts {
36+
var account types.AccountI
37+
err := suite.app.InterfaceRegistry().UnpackAny(acc, &account)
38+
suite.Require().NoError(err)
39+
40+
suite.Require().True(
41+
first.Equals(account.GetAddress()) || second.Equals(account.GetAddress()))
42+
}
43+
},
44+
},
45+
}
46+
47+
for _, tc := range testCases {
48+
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
49+
suite.SetupTest() // reset
50+
51+
tc.malleate()
52+
ctx := sdk.WrapSDKContext(suite.ctx)
53+
54+
res, err := suite.queryClient.Accounts(ctx, req)
55+
56+
if tc.expPass {
57+
suite.Require().NoError(err)
58+
suite.Require().NotNil(res)
59+
} else {
60+
suite.Require().Error(err)
61+
suite.Require().Nil(res)
62+
}
63+
64+
tc.posttests(res)
65+
})
66+
}
67+
}
68+
1169
func (suite *KeeperTestSuite) TestGRPCQueryAccount() {
1270
var (
1371
req *types.QueryAccountRequest

0 commit comments

Comments
 (0)