Merge pull request #361 from CosmWasm/getAccount-query

Add StargateClient.getAccount
This commit is contained in:
mergify[bot] 2020-08-10 13:13:17 +00:00 committed by GitHub
commit fcd917d2c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 135 additions and 22 deletions

View File

@ -84,7 +84,7 @@ export {
uint64ToString, uint64ToString,
} from "./lcdapi"; } from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; export { decodeAminoPubkey, decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey";
export { findSequenceForSignedTx } from "./sequence"; export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature"; export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient"; export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";

View File

@ -21,9 +21,7 @@ const pubkeyAminoPrefixEd25519 = fromHex("1624de6420");
const pubkeyAminoPrefixSr25519 = fromHex("0dfb1005"); const pubkeyAminoPrefixSr25519 = fromHex("0dfb1005");
const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length; const pubkeyAminoPrefixLength = pubkeyAminoPrefixSecp256k1.length;
export function decodeBech32Pubkey(bechEncoded: string): PubKey { export function decodeAminoPubkey(data: Uint8Array): PubKey {
const { data } = Bech32.decode(bechEncoded);
const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength); const aminoPrefix = data.slice(0, pubkeyAminoPrefixLength);
const rest = data.slice(pubkeyAminoPrefixLength); const rest = data.slice(pubkeyAminoPrefixLength);
if (equal(aminoPrefix, pubkeyAminoPrefixSecp256k1)) { if (equal(aminoPrefix, pubkeyAminoPrefixSecp256k1)) {
@ -55,6 +53,11 @@ export function decodeBech32Pubkey(bechEncoded: string): PubKey {
} }
} }
export function decodeBech32Pubkey(bechEncoded: string): PubKey {
const { data } = Bech32.decode(bechEncoded);
return decodeAminoPubkey(data);
}
export function encodeBech32Pubkey(pubkey: PubKey, prefix: string): string { export function encodeBech32Pubkey(pubkey: PubKey, prefix: string): string {
let aminoPrefix: Uint8Array; let aminoPrefix: Uint8Array;
switch (pubkey.type) { switch (pubkey.type) {

View File

@ -82,7 +82,7 @@ export {
uint64ToString, uint64ToString,
} from "./lcdapi"; } from "./lcdapi";
export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs";
export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; export { decodeAminoPubkey, decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey";
export { findSequenceForSignedTx } from "./sequence"; export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature"; export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { FeeTable, SigningCosmosClient } from "./signingcosmosclient"; export { FeeTable, SigningCosmosClient } from "./signingcosmosclient";

View File

@ -1,4 +1,5 @@
import { PubKey } from "./types"; import { PubKey } from "./types";
export declare function encodeSecp256k1Pubkey(pubkey: Uint8Array): PubKey; export declare function encodeSecp256k1Pubkey(pubkey: Uint8Array): PubKey;
export declare function decodeAminoPubkey(data: Uint8Array): PubKey;
export declare function decodeBech32Pubkey(bechEncoded: string): PubKey; export declare function decodeBech32Pubkey(bechEncoded: string): PubKey;
export declare function encodeBech32Pubkey(pubkey: PubKey, prefix: string): string; export declare function encodeBech32Pubkey(pubkey: PubKey, prefix: string): string;

View File

@ -1,5 +1,7 @@
import { assert } from "@cosmjs/utils";
import { StargateClient } from "./stargateclient"; import { StargateClient } from "./stargateclient";
import { nonExistentAddress, pendingWithoutSimapp, simapp, unused } from "./testutils.spec"; import { nonExistentAddress, pendingWithoutSimapp, simapp, unused, validator } from "./testutils.spec";
describe("StargateClient", () => { describe("StargateClient", () => {
describe("connect", () => { describe("connect", () => {
@ -11,14 +13,71 @@ describe("StargateClient", () => {
}); });
}); });
describe("getAccount", () => {
it("works for unused account", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const account = await client.getAccount(unused.address);
assert(account);
expect(account).toEqual({
address: unused.address,
pubkey: null,
accountNumber: unused.accountNumber,
sequence: unused.sequence,
});
client.disconnect();
});
it("works for account with pubkey and non-zero sequence", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const account = await client.getAccount(validator.address);
assert(account);
expect(account).toEqual({
address: validator.address,
pubkey: validator.pubkey,
accountNumber: validator.accountNumber,
sequence: validator.sequence,
});
client.disconnect();
});
it("returns null for non-existent address", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const account = await client.getAccount(nonExistentAddress);
expect(account).toBeNull();
client.disconnect();
});
});
describe("getSequence", () => { describe("getSequence", () => {
it("works for unused account", async () => { it("works for unused account", async () => {
pendingWithoutSimapp(); pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl); const client = await StargateClient.connect(simapp.tendermintUrl);
const { accountNumber, sequence } = await client.getSequence(unused.address); const account = await client.getSequence(unused.address);
expect(accountNumber).toEqual(unused.accountNumber); assert(account);
expect(sequence).toEqual(unused.sequence); expect(account).toEqual({
accountNumber: unused.accountNumber,
sequence: unused.sequence,
});
client.disconnect();
});
it("returns null for non-existent address", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const account = await client.getSequence(nonExistentAddress);
expect(account).toBeNull();
client.disconnect(); client.disconnect();
}); });

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
import { Bech32, toAscii, toHex } from "@cosmjs/encoding"; import { Bech32, toAscii, toHex } from "@cosmjs/encoding";
import { Coin } from "@cosmjs/launchpad"; import { Coin, decodeAminoPubkey, PubKey } from "@cosmjs/launchpad";
import { Uint64 } from "@cosmjs/math"; import { Uint64 } from "@cosmjs/math";
import { decodeAny } from "@cosmjs/proto-signing"; import { decodeAny } from "@cosmjs/proto-signing";
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc"; import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
@ -9,7 +9,15 @@ import Long from "long";
import { cosmos } from "./generated/codecimpl"; import { cosmos } from "./generated/codecimpl";
export interface GetSequenceResult { export interface Account {
/** Bech32 account address */
readonly address: string;
readonly pubkey: PubKey | null;
readonly accountNumber: number;
readonly sequence: number;
}
export interface SequenceResponse {
readonly accountNumber: number; readonly accountNumber: number;
readonly sequence: number; readonly sequence: number;
} }
@ -18,6 +26,18 @@ function uint64FromProto(input: number | Long): Uint64 {
return Uint64.fromString(input.toString()); return Uint64.fromString(input.toString());
} }
function decodeBaseAccount(data: Uint8Array, prefix: string): Account {
const { address, pubKey, accountNumber, sequence } = cosmos.auth.BaseAccount.decode(data);
// Pubkey is still Amino-encoded in BaseAccount (https://github.com/cosmos/cosmos-sdk/issues/6886)
const pubkey = pubKey.length ? decodeAminoPubkey(pubKey) : null;
return {
address: Bech32.encode(prefix, address),
pubkey: pubkey,
accountNumber: uint64FromProto(accountNumber).toNumber(),
sequence: uint64FromProto(sequence).toNumber(),
};
}
function coinFromProto(input: cosmos.ICoin): Coin { function coinFromProto(input: cosmos.ICoin): Coin {
assertDefined(input.amount); assertDefined(input.amount);
assertDefined(input.denom); assertDefined(input.denom);
@ -41,23 +61,33 @@ export class StargateClient {
this.tmClient = tmClient; this.tmClient = tmClient;
} }
public async getSequence(address: string): Promise<GetSequenceResult> { public async getAccount(searchAddress: string): Promise<Account | null> {
const binAddress = Bech32.decode(address).data; const { prefix, data: binAddress } = Bech32.decode(searchAddress);
// https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32 // https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32
const accountKey = Uint8Array.from([0x01, ...binAddress]); const accountKey = Uint8Array.from([0x01, ...binAddress]);
const responseData = await this.queryVerified("acc", accountKey); const responseData = await this.queryVerified("acc", accountKey);
if (responseData.length === 0) return null;
const { typeUrl, value } = decodeAny(responseData); const { typeUrl, value } = decodeAny(responseData);
switch (typeUrl) { switch (typeUrl) {
case "/cosmos.auth.BaseAccount": { case "/cosmos.auth.BaseAccount": {
const { accountNumber, sequence } = cosmos.auth.BaseAccount.decode(value); return decodeBaseAccount(value, prefix);
return {
accountNumber: uint64FromProto(accountNumber).toNumber(),
sequence: uint64FromProto(sequence).toNumber(),
};
} }
default: default:
throw new Error(`Unsupported type: ${typeUrl}`); throw new Error(`Unsupported type: '${typeUrl}'`);
}
}
public async getSequence(address: string): Promise<SequenceResponse | null> {
const account = await this.getAccount(address);
if (account) {
return {
accountNumber: account.accountNumber,
sequence: account.sequence,
};
} else {
return null;
} }
} }

View File

@ -24,4 +24,16 @@ export const unused = {
balanceFee: "1000000000", // 1000 COSM balanceFee: "1000000000", // 1000 COSM
}; };
export const validator = {
/** From first gentx's auth_info.signer_infos in scripts/simapp/template/.simapp/config/genesis.json */
pubkey: {
type: "tendermint/PubKeySecp256k1",
value: "AnFadRAdh6Fl7robHe8jywDMKSWQQjB7SlpoqGsX9Ghw",
},
/** delegator_address from /cosmos.staking.MsgCreateValidator in scripts/simapp/template/.simapp/config/genesis.json */
address: "cosmos12gm9sa666hywxu9nzzmp7hyl7a55hvg769w2kz",
accountNumber: 0,
sequence: 1,
};
export const nonExistentAddress = "cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd"; export const nonExistentAddress = "cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd";

View File

@ -1,5 +1,12 @@
import { Coin } from "@cosmjs/launchpad"; import { Coin, PubKey } from "@cosmjs/launchpad";
export interface GetSequenceResult { export interface Account {
/** Bech32 account address */
readonly address: string;
readonly pubkey: PubKey | null;
readonly accountNumber: number;
readonly sequence: number;
}
export interface SequenceResponse {
readonly accountNumber: number; readonly accountNumber: number;
readonly sequence: number; readonly sequence: number;
} }
@ -7,7 +14,8 @@ export declare class StargateClient {
private readonly tmClient; private readonly tmClient;
static connect(endpoint: string): Promise<StargateClient>; static connect(endpoint: string): Promise<StargateClient>;
private constructor(); private constructor();
getSequence(address: string): Promise<GetSequenceResult>; getAccount(searchAddress: string): Promise<Account | null>;
getSequence(address: string): Promise<SequenceResponse | null>;
getBalance(address: string, searchDenom: string): Promise<Coin | null>; getBalance(address: string, searchDenom: string): Promise<Coin | null>;
/** /**
* Queries all balances for all denoms that belong to this address. * Queries all balances for all denoms that belong to this address.