Skip to content

Commit

Permalink
feat(cli): add acc-address by id cli and grpc gateway api (backport c…
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored and JeancarloBarrios committed Sep 28, 2024
1 parent 31916b1 commit 93d38bf
Show file tree
Hide file tree
Showing 17 changed files with 1,930 additions and 537 deletions.
1,278 changes: 960 additions & 318 deletions api/cosmos/auth/v1beta1/query.pulsar.go

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions api/cosmos/auth/v1beta1/query_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion baseapp/block_gas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func TestBaseApp_BlockGas(t *testing.T) {
require.Equal(t, []byte("ok"), okValue)
}
// check block gas is always consumed
baseGas := uint64(59142) // baseGas is the gas consumed before tx msg
baseGas := uint64(70184) // baseGas is the gas consumed before tx msg
expGasConsumed := addUint64Saturating(tc.gasToConsume, baseGas)
if expGasConsumed > txtypes.MaxGasWanted {
// capped by gasLimit
Expand Down
25 changes: 21 additions & 4 deletions proto/cosmos/auth/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ service Query {
option (google.api.http).get = "/cosmos/auth/v1beta1/address_by_id/{account_id}";
}

// AccountAddressByID returns account address based on account id
rpc AccountAddressByID(QueryAccountAddressByIDRequest) returns (QueryAccountAddressByIDResponse) {
option (google.api.http).get = "/cosmos/auth/v1beta1/address_by_id/{id}";
}

// Params queries all parameters.
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (cosmos.query.v1.module_query_safe) = true;
Expand Down Expand Up @@ -135,7 +140,19 @@ message QueryModuleAccountByNameRequest {
string name = 1;
}

// QueryModuleAccountByNameResponse is the response type for the Query/ModuleAccountByName RPC method.
message QueryModuleAccountByNameResponse {
google.protobuf.Any account = 1 [(cosmos_proto.accepts_interface) = "ModuleAccountI"];
}
// AddressStringToBytesResponse is the response type for AddressBytes rpc method.
//
// Since: cosmos-sdk 0.46
message AddressStringToBytesResponse {
bytes address_bytes = 1;
}

// QueryAccountAddressByIDRequest is the request type for AccountAddressByID rpc method
message QueryAccountAddressByIDRequest{
int64 id = 1;
}

// QueryAccountAddressByIDResponse is the response type for AccountAddressByID rpc method
message QueryAccountAddressByIDResponse {
string account_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}
113 changes: 109 additions & 4 deletions tests/e2e/auth/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authcli "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
authclitestutil "github.com/cosmos/cosmos-sdk/x/auth/client/testutil"
"github.com/cosmos/cosmos-sdk/x/auth/types"
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"
"github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
)

Expand Down Expand Up @@ -267,9 +271,110 @@ func (s *E2ETestSuite) TestCLISignBatch() {
_, err = authclitestutil.TxSignBatchExec(clientCtx, val.GetAddress(), malformedFile.Name(), fmt.Sprintf("--%s=%s", flags.FlagChainID, clientCtx.ChainID), "--signature-only")
s.Require().Error(err)

// make a txn to increase the sequence of sender
_, seq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, val.GetAddress())
s.Require().NoError(err)
func (s *IntegrationTestSuite) TestCliGetAccountAddressByID() {
require := s.Require()
val1 := s.network.Validators[0]
testCases := []struct {
name string
args []string
expectErr bool
}{
{
"not enough args",
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
true,
},
{
"invalid account id",
[]string{fmt.Sprint(-1), fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
true,
},
{
"valid account id",
[]string{fmt.Sprint(0), fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
false,
},
}

for _, tc := range testCases {
tc := tc
s.Run(tc.name, func() {
cmd := authcli.GetAccountAddressByIDCmd()
clientCtx := val1.ClientCtx

queryResJSON, err := clitestutil.ExecTestCLICmd(clientCtx, cmd, tc.args)
if tc.expectErr {
s.Require().Error(err)
} else {
s.Require().NoError(err)
var res types.QueryAccountAddressByIDResponse
require.NoError(val1.ClientCtx.Codec.UnmarshalJSON(queryResJSON.Bytes(), &res))
require.NotNil(res.GetAccountAddress())
}
})
}
}

func (s *IntegrationTestSuite) TestCLISignAminoJSON() {
require := s.Require()
val1 := s.network.Validators[0]
txCfg := val1.ClientCtx.TxConfig
sendTokens := sdk.NewCoins(
sdk.NewCoin(fmt.Sprintf("%stoken", val1.Moniker), sdk.NewInt(10)),
sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(10)),
)
txBz, err := s.createBankMsg(val1, val1.Address,
sendTokens, fmt.Sprintf("--%s=true", flags.FlagGenerateOnly))
require.NoError(err)
fileUnsigned := testutil.WriteToNewTempFile(s.T(), txBz.String())
chainFlag := fmt.Sprintf("--%s=%s", flags.FlagChainID, val1.ClientCtx.ChainID)
sigOnlyFlag := "--signature-only"
signModeAminoFlag := "--sign-mode=amino-json"

// SIC! validators have same key names and same addresses as those registered in the keyring,
// BUT the keys are different!
valRecord, err := val1.ClientCtx.Keyring.Key(val1.Moniker)
require.NoError(err)

// query account info
queryResJSON, err := QueryAccountExec(val1.ClientCtx, val1.Address)
require.NoError(err)
var account authtypes.AccountI
require.NoError(val1.ClientCtx.Codec.UnmarshalInterfaceJSON(queryResJSON.Bytes(), &account))

/**** test signature-only ****/
res, err := TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag,
sigOnlyFlag, signModeAminoFlag)
require.NoError(err)
pub, err := valRecord.GetPubKey()
require.NoError(err)
checkSignatures(require, txCfg, res.Bytes(), pub)
sigs, err := txCfg.UnmarshalSignatureJSON(res.Bytes())
require.NoError(err)
require.Equal(1, len(sigs))
require.Equal(account.GetSequence(), sigs[0].Sequence)

/**** test full output ****/
res, err = TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, signModeAminoFlag)
require.NoError(err)

// txCfg.UnmarshalSignatureJSON can't unmarshal a fragment of the signature, so we create this structure.
type txFragment struct {
Signatures []json.RawMessage
}
var txOut txFragment
err = json.Unmarshal(res.Bytes(), &txOut)
require.NoError(err)
require.Len(txOut.Signatures, 1)

/**** test file output ****/
filenameSigned := filepath.Join(s.T().TempDir(), "test_sign_out.json")
fileFlag := fmt.Sprintf("--%s=%s", flags.FlagOutputDocument, filenameSigned)
_, err = TxSignExec(val1.ClientCtx, val1.Address, fileUnsigned.Name(), chainFlag, fileFlag, signModeAminoFlag)
require.NoError(err)
fContent, err := os.ReadFile(filenameSigned)
require.NoError(err)
require.Equal(res.String(), string(fContent))

account1, err := clientCtx.Keyring.Key("newAccount1")
s.Require().NoError(err)
Expand Down
5 changes: 3 additions & 2 deletions x/auth/ante/ante_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,8 +705,9 @@ func TestAnteHandlerMemoGas(t *testing.T) {
},
{
"tx with memo has enough gas",
func(suite *AnteTestSuite) TestCaseArgs {
accs := suite.CreateTestAccounts(1)
func() {
feeAmount = sdk.NewCoins(sdk.NewInt64Coin("atom", 0))
gasLimit = 60000
suite.txBuilder.SetMemo(strings.Repeat("0123456789", 10))
return TestCaseArgs{
feeAmount: sdk.NewCoins(sdk.NewInt64Coin("atom", 0)),
Expand Down
46 changes: 45 additions & 1 deletion x/auth/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"context"
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -40,6 +41,7 @@ func GetQueryCmd() *cobra.Command {

cmd.AddCommand(
GetAccountCmd(),
GetAccountAddressByIDCmd(),
GetAccountsCmd(),
QueryParamsCmd(),
QueryModuleAccountByNameCmd(),
Expand Down Expand Up @@ -133,7 +135,49 @@ $ %s query tx --%s=%s <sig1_base64>,<sig2_base64...>
return fmt.Errorf("no transaction found with hash %s", args[0])
}

return clientCtx.PrintProto(output)
// GetAccountAddressByIDCmd returns a query account that will display the account address of a given account id.
func GetAccountAddressByIDCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "address-by-id [id]",
Short: "Query for account address by account id",
Args: cobra.ExactArgs(1),
Example: fmt.Sprintf("%s q auth address-by-id 1", version.AppName),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

id, err := strconv.ParseInt(args[0], 10, 64)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.AccountAddressByID(cmd.Context(), &types.QueryAccountAddressByIDRequest{Id: id})
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// GetAccountsCmd returns a query command that will display a list of accounts
func GetAccountsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "accounts",
Short: "Query all the accounts",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

case TypeSig:
sigParts, err := ParseSigArgs(args)
Expand Down
20 changes: 20 additions & 0 deletions x/auth/keeper/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ func (ak AccountKeeper) HasAccount(ctx sdk.Context, addr sdk.AccAddress) bool {
return store.Has(types.AddressStoreKey(addr))
}

// HasAccountAddressByID checks account address exists by id.
func (ak AccountKeeper) HasAccountAddressByID(ctx sdk.Context, id uint64) bool {
store := ctx.KVStore(ak.key)
return store.Has(types.AccountNumberStoreKey(id))
}

// GetAccount implements AccountKeeperI.
func (ak accountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI {
store := ctx.KVStore(ak.key)
Expand All @@ -46,6 +52,16 @@ func (ak accountKeeper) GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.A
return acc
}

// GetAccountAddressById returns account address by id.
func (ak AccountKeeper) GetAccountAddressByID(ctx sdk.Context, id uint64) string {
store := ctx.KVStore(ak.key)
bz := store.Get(types.AccountNumberStoreKey(id))
if bz == nil {
return ""
}
return sdk.AccAddress(bz).String()
}

// GetAllAccounts returns all accounts in the accountKeeper.
func (ak accountKeeper) GetAllAccounts(ctx sdk.Context) (accounts []types.AccountI) {
ak.IterateAccounts(ctx, func(acc types.AccountI) (stop bool) {
Expand All @@ -71,6 +87,9 @@ func (ak accountKeeper) SetAccount(ctx sdk.Context, acc types.AccountI) {
if err != nil {
panic(err)
}

store.Set(types.AddressStoreKey(addr), bz)
store.Set(types.AccountNumberStoreKey(acc.GetAccountNumber()), addr.Bytes())
}

// RemoveAccount removes an account for the account mapper store.
Expand All @@ -79,6 +98,7 @@ func (ak accountKeeper) RemoveAccount(ctx sdk.Context, acc types.AccountI) {
addr := acc.GetAddress()
store := ctx.KVStore(ak.key)
store.Delete(types.AddressStoreKey(addr))
store.Delete(types.AccountNumberStoreKey(acc.GetAccountNumber()))
}

// IterateAccounts iterates over all the stored accounts and performs a callback function.
Expand Down
20 changes: 19 additions & 1 deletion x/auth/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,25 @@ import (

var _ types.QueryServer = accountKeeper{}

func (ak accountKeeper) Accounts(c context.Context, req *types.QueryAccountsRequest) (*types.QueryAccountsResponse, error) {
func (ak AccountKeeper) AccountAddressByID(c context.Context, req *types.QueryAccountAddressByIDRequest) (*types.QueryAccountAddressByIDResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

if req.Id < 0 {
return nil, status.Error(codes.InvalidArgument, "Invalid account id")
}

ctx := sdk.UnwrapSDKContext(c)
address := ak.GetAccountAddressByID(ctx, uint64(req.GetId()))
if len(address) == 0 {
return nil, status.Errorf(codes.NotFound, "account address not found with id %d", req.Id)
}

return &types.QueryAccountAddressByIDResponse{AccountAddress: address}, nil
}

func (ak AccountKeeper) Accounts(c context.Context, req *types.QueryAccountsRequest) (*types.QueryAccountsResponse, error) {
if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}
Expand Down
Loading

0 comments on commit 93d38bf

Please sign in to comment.