diff --git a/chain/cosmos/account_retriever.go b/chain/cosmos/account_retriever.go new file mode 100644 index 000000000..66b9f66cf --- /dev/null +++ b/chain/cosmos/account_retriever.go @@ -0,0 +1,88 @@ +package cosmos + +import ( + "context" + "fmt" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "strconv" + + grpc "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" + grpctypes "github.com/cosmos/cosmos-sdk/types/grpc" +) + +var ( + _ client.Account = sdk.AccountI(nil) + _ client.AccountRetriever = AccountRetriever{} +) + +// AccountRetriever defines the properties of a type that can be used to +// retrieve accounts. +type AccountRetriever struct { + chain *CosmosChain +} + +// GetAccount queries for an account given an address and a block height. An +// error is returned if the query or decoding fails. +func (ar AccountRetriever) GetAccount(clientCtx client.Context, addr sdk.AccAddress) (client.Account, error) { + account, _, err := ar.GetAccountWithHeight(clientCtx, addr) + return account, err +} + +// GetAccountWithHeight queries for an account given an address. Returns the +// height of the query with the account. An error is returned if the query +// or decoding fails. +func (ar AccountRetriever) GetAccountWithHeight(clientCtx client.Context, addr sdk.AccAddress) (client.Account, int64, error) { + var header metadata.MD + + bech32Address, err := ar.chain.AccAddressToBech32(addr) + if err != nil { + return nil, 0, err + } + + queryClient := authtypes.NewQueryClient(clientCtx) + res, err := queryClient.Account(context.Background(), &authtypes.QueryAccountRequest{Address: bech32Address}, grpc.Header(&header)) + if err != nil { + return nil, 0, err + } + + blockHeight := header.Get(grpctypes.GRPCBlockHeightHeader) + if l := len(blockHeight); l != 1 { + return nil, 0, fmt.Errorf("unexpected '%s' header length; got %d, expected: %d", grpctypes.GRPCBlockHeightHeader, l, 1) + } + + nBlockHeight, err := strconv.Atoi(blockHeight[0]) + if err != nil { + return nil, 0, fmt.Errorf("failed to parse block height: %w", err) + } + + var acc sdk.AccountI + if err := clientCtx.InterfaceRegistry.UnpackAny(res.Account, &acc); err != nil { + return nil, 0, err + } + + return acc, int64(nBlockHeight), nil +} + +// EnsureExists returns an error if no account exists for the given address else nil. +func (ar AccountRetriever) EnsureExists(clientCtx client.Context, addr sdk.AccAddress) error { + if _, err := ar.GetAccount(clientCtx, addr); err != nil { + return err + } + + return nil +} + +// GetAccountNumberSequence returns sequence and account number for the given address. +// It returns an error if the account couldn't be retrieved from the state. +func (ar AccountRetriever) GetAccountNumberSequence(clientCtx client.Context, addr sdk.AccAddress) (uint64, uint64, error) { + acc, err := ar.GetAccount(clientCtx, addr) + if err != nil { + return 0, 0, err + } + + return acc.GetAccountNumber(), acc.GetSequence(), nil +} diff --git a/chain/cosmos/address.go b/chain/cosmos/address.go index 4df36e791..121c021bb 100644 --- a/chain/cosmos/address.go +++ b/chain/cosmos/address.go @@ -2,6 +2,7 @@ package cosmos import ( "errors" + "github.com/cosmos/cosmos-sdk/types/bech32" "strings" sdk "github.com/cosmos/cosmos-sdk/types" @@ -26,3 +27,7 @@ func (c *CosmosChain) AccAddressFromBech32(address string) (addr sdk.AccAddress, return sdk.AccAddress(bz), nil } + +func (c *CosmosChain) AccAddressToBech32(addr sdk.AccAddress) (string, error) { + return bech32.ConvertAndEncode(c.Config().Bech32Prefix, addr) +} diff --git a/chain/cosmos/broadcaster.go b/chain/cosmos/broadcaster.go index db4b52d01..4989fc085 100644 --- a/chain/cosmos/broadcaster.go +++ b/chain/cosmos/broadcaster.go @@ -15,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/strangelove-ventures/interchaintest/v8/internal/dockerutil" "github.com/strangelove-ventures/interchaintest/v8/testutil" ) @@ -165,7 +164,7 @@ func (b *Broadcaster) defaultClientContext(fromUser User, sdkAdd sdk.AccAddress) WithFromAddress(sdkAdd). WithFromName(fromUser.KeyName()). WithSkipConfirmation(true). - WithAccountRetriever(authtypes.AccountRetriever{}). + WithAccountRetriever(AccountRetriever{chain: b.chain}). WithKeyring(kr). WithBroadcastMode(flags.BroadcastSync). WithCodec(b.chain.cfg.EncodingConfig.Codec)