Merge pull request #741 from cosmos/713-harmonize-getAccount

Harmonise getAccount methods
This commit is contained in:
Will Clark 2021-03-30 16:32:09 +01:00 committed by GitHub
commit 774bab681b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 102 additions and 80 deletions

View File

@ -77,6 +77,20 @@ and this project adheres to
`Bech32.encode(prefix, rawSecp256k1PubkeyToRawAddress(pubkeyRaw))` with
`rawSecp256k1PubkeyToRawAddress` from @cosmjs/amino.
- @cosmjs/stargate: `parseRawLog` is now nested under the `logs` export.
- @cosmjs/stargate: `auth` extension now has unverified queries at the root and
verified queries nested under `.verified`.
- @cosmjs/stargate: `StargateClient.getAccount` now uses an unverified query and
`StargateClient.getAccountUnverified` has been removed.
`StargateClient.getAccountVerified` has been added, which performs a verified
query.
- @cosmjs/cosmwasm-stargate: `CosmWasmClient.getAccount` now uses an unverified
query and `CosmWasmClient.getAccountUnverified` has been removed.
`CosmWasmClient.getAccountVerified` has been added, which performs a verified
query.
- @cosmjs/stargate: `StargateClient.getSequence` now rejects if the account is
not found, instead of returning null.
- @cosmjs/cosmwasm-stargate: `CosmWasmClient.getSequence` now rejects if the
account is not found, instead of returning null.
### Deprecated

View File

@ -82,24 +82,6 @@ describe("CosmWasmClient", () => {
});
});
describe("getSequence", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = await CosmWasmClient.connect(wasmd.endpoint);
expect(await client.getSequence(unused.address)).toEqual({
accountNumber: unused.accountNumber,
sequence: unused.sequence,
});
});
it("returns null for missing accounts", async () => {
pendingWithoutWasmd();
const client = await CosmWasmClient.connect(wasmd.endpoint);
const missing = makeRandomAddress();
expect(await client.getSequence(missing)).toBeNull();
});
});
describe("getAccount", () => {
it("works", async () => {
pendingWithoutWasmd();
@ -120,6 +102,26 @@ describe("CosmWasmClient", () => {
});
});
describe("getSequence", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = await CosmWasmClient.connect(wasmd.endpoint);
expect(await client.getSequence(unused.address)).toEqual({
accountNumber: unused.accountNumber,
sequence: unused.sequence,
});
});
it("rejects for missing accounts", async () => {
pendingWithoutWasmd();
const client = await CosmWasmClient.connect(wasmd.endpoint);
const missing = makeRandomAddress();
await expectAsync(client.getSequence(missing)).toBeRejectedWithError(
/account does not exist on chain/i,
);
});
});
describe("getBlock", () => {
it("works for latest block", async () => {
pendingWithoutWasmd();

View File

@ -109,20 +109,28 @@ export class CosmWasmClient {
}
public async getAccount(searchAddress: string): Promise<Account | null> {
const account = await this.forceGetQueryClient().auth.account(searchAddress);
return account ? accountFromAny(account) : null;
try {
const account = await this.forceGetQueryClient().auth.account(searchAddress);
return account ? accountFromAny(account) : null;
} catch (error) {
if (/rpc error: code = NotFound/i.test(error)) {
return null;
}
throw error;
}
}
public async getSequence(address: string): Promise<SequenceResponse | null> {
public async getSequence(address: string): Promise<SequenceResponse> {
const account = await this.getAccount(address);
if (account) {
return {
accountNumber: account.accountNumber,
sequence: account.sequence,
};
} else {
return null;
if (!account) {
throw new Error(
"Account does not exist on chain. Send some tokens there before trying to query sequence.",
);
}
return {
accountNumber: account.accountNumber,
sequence: account.sequence,
};
}
public async getBlock(height?: number): Promise<Block> {

View File

@ -409,11 +409,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
throw new Error("Failed to retrieve account from signer");
}
const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.pubkey));
const accountFromChain = await this.getAccount(signerAddress);
if (!accountFromChain) {
throw new Error("Account not found");
}
const { accountNumber, sequence } = accountFromChain;
const { accountNumber, sequence } = await this.getSequence(signerAddress);
const chainId = await this.getChainId();
const txBody = {
messages: messages,

View File

@ -53,23 +53,24 @@ describe("AuthExtension", () => {
tmClient.disconnect();
});
it("returns null for non-existent address", async () => {
it("rejects for non-existent address", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.account(nonExistentAddress);
expect(account).toBeNull();
await expectAsync(client.auth.account(nonExistentAddress)).toBeRejectedWithError(
/account cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd not found/i,
);
tmClient.disconnect();
});
});
describe("unverified", () => {
describe("verified", () => {
describe("account", () => {
it("works for unused account", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.unverified.account(unused.address);
const account = await client.auth.verified.account(unused.address);
assert(account);
expect(account.typeUrl).toEqual("/cosmos.auth.v1beta1.BaseAccount");
@ -86,7 +87,7 @@ describe("AuthExtension", () => {
it("works for account with pubkey and non-zero sequence", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.unverified.account(validator.delegatorAddress);
const account = await client.auth.verified.account(validator.delegatorAddress);
assert(account);
expect(account.typeUrl).toEqual("/cosmos.auth.v1beta1.BaseAccount");
@ -103,10 +104,9 @@ describe("AuthExtension", () => {
it("returns null for non-existent address", async () => {
pendingWithoutSimapp();
const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl);
const account = await client.auth.verified.account(nonExistentAddress);
await expectAsync(client.auth.unverified.account(nonExistentAddress)).toBeRejectedWithError(
/account cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd not found/i,
);
expect(account).toBeNull();
tmClient.disconnect();
});

View File

@ -13,7 +13,7 @@ export interface AuthExtension {
* `typeUrl` and decode the `value` using its own type decoder.
*/
readonly account: (address: string) => Promise<Any | null>;
readonly unverified: {
readonly verified: {
/**
* Returns an account if it exists and `null` otherwise.
*
@ -35,16 +35,16 @@ export function setupAuthExtension(base: QueryClient): AuthExtension {
return {
auth: {
account: async (address: string) => {
// https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32
const key = Uint8Array.from([0x01, ...toAccAddress(address)]);
const responseData = await base.queryVerified("acc", key);
if (responseData.length === 0) return null;
return Any.decode(responseData);
const { account } = await queryService.Account({ address: address });
return account ?? null;
},
unverified: {
verified: {
account: async (address: string) => {
const { account } = await queryService.Account({ address: address });
return account ?? null;
// https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32
const key = Uint8Array.from([0x01, ...toAccAddress(address)]);
const responseData = await base.queryVerified("acc", key);
if (responseData.length === 0) return null;
return Any.decode(responseData);
},
},
},

View File

@ -276,11 +276,7 @@ export class SigningStargateClient extends StargateClient {
if (explicitSignerData) {
signerData = explicitSignerData;
} else {
const accountFromChain = await this.getAccountUnverified(signerAddress);
if (!accountFromChain) {
throw new Error("Account not found");
}
const { accountNumber, sequence } = accountFromChain;
const { accountNumber, sequence } = await this.getSequence(signerAddress);
const chainId = await this.getChainId();
signerData = { accountNumber, sequence, chainId };
}

View File

@ -131,12 +131,13 @@ describe("StargateClient", () => {
client.disconnect();
});
it("returns null for non-existent address", async () => {
it("rejects for non-existent address", async () => {
pendingWithoutSimapp();
const client = await StargateClient.connect(simapp.tendermintUrl);
const account = await client.getSequence(nonExistentAddress);
expect(account).toBeNull();
await expectAsync(client.getSequence(nonExistentAddress)).toBeRejectedWithError(
/account does not exist on chain/i,
);
client.disconnect();
});

View File

@ -160,31 +160,36 @@ export class StargateClient {
return status.syncInfo.latestBlockHeight;
}
// this is nice to display data to the user, but is slower
public async getAccount(searchAddress: string): Promise<Account | null> {
const account = await this.forceGetQueryClient().auth.account(searchAddress);
return account ? accountFromAny(account) : null;
}
// if we just need to get the sequence for signing a transaction, let's make this faster
// (no need to wait a block before submitting)
public async getAccountUnverified(searchAddress: string): Promise<Account | null> {
const account = await this.forceGetQueryClient().auth.unverified.account(searchAddress);
return account ? accountFromAny(account) : null;
}
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;
try {
const account = await this.forceGetQueryClient().auth.account(searchAddress);
return account ? accountFromAny(account) : null;
} catch (error) {
if (/rpc error: code = NotFound/i.test(error)) {
return null;
}
throw error;
}
}
public async getAccountVerified(searchAddress: string): Promise<Account | null> {
const account = await this.forceGetQueryClient().auth.verified.account(searchAddress);
return account ? accountFromAny(account) : null;
}
public async getSequence(address: string): Promise<SequenceResponse> {
const account = await this.getAccount(address);
if (!account) {
throw new Error(
"Account does not exist on chain. Send some tokens there before trying to query sequence.",
);
}
return {
accountNumber: account.accountNumber,
sequence: account.sequence,
};
}
public async getBlock(height?: number): Promise<Block> {
const response = await this.forceGetTmClient().block(height);
return {