Create AuthExtension
This commit is contained in:
parent
5ccc71c261
commit
4003f5eb65
96
packages/stargate/src/queries/auth.spec.ts
Normal file
96
packages/stargate/src/queries/auth.spec.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { encodeAminoPubkey } from "@cosmjs/launchpad";
|
||||
import { Client as TendermintClient } from "@cosmjs/tendermint-rpc";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
import Long from "long";
|
||||
|
||||
import { nonExistentAddress, pendingWithoutSimapp, simapp, unused, validator } from "../testutils.spec";
|
||||
import { AuthExtension, setupAuthExtension } from "./auth";
|
||||
import { QueryClient } from "./queryclient";
|
||||
import { toAccAddress } from "./utils";
|
||||
|
||||
async function makeAuthClient(rpcUrl: string): Promise<QueryClient & AuthExtension> {
|
||||
// TODO: tmClient is not owned by QueryClient but should be disconnected somehow (once we use WebSockets)
|
||||
const tmClient = await TendermintClient.connect(rpcUrl);
|
||||
return QueryClient.withExtensions(tmClient, setupAuthExtension);
|
||||
}
|
||||
|
||||
describe("AuthExtension", () => {
|
||||
describe("account", () => {
|
||||
it("works for unused account", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.account(unused.address);
|
||||
assert(account);
|
||||
expect(account).toEqual({
|
||||
address: toAccAddress(unused.address),
|
||||
// pubKey not set
|
||||
accountNumber: Long.fromNumber(unused.accountNumber, true),
|
||||
// sequence not set
|
||||
});
|
||||
});
|
||||
|
||||
it("works for account with pubkey and non-zero sequence", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.account(validator.address);
|
||||
assert(account);
|
||||
expect(account).toEqual({
|
||||
address: toAccAddress(validator.address),
|
||||
pubKey: encodeAminoPubkey(validator.pubkey),
|
||||
// accountNumber not set
|
||||
sequence: Long.fromNumber(validator.sequence, true),
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null for non-existent address", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.account(nonExistentAddress);
|
||||
expect(account).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("unverified", () => {
|
||||
describe("account", () => {
|
||||
it("works for unused account", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.unverified.account(unused.address);
|
||||
assert(account);
|
||||
expect(account).toEqual({
|
||||
address: toAccAddress(unused.address),
|
||||
// pubKey not set
|
||||
accountNumber: Long.fromNumber(unused.accountNumber, true),
|
||||
// sequence not set
|
||||
});
|
||||
});
|
||||
|
||||
it("works for account with pubkey and non-zero sequence", async () => {
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.unverified.account(validator.address);
|
||||
assert(account);
|
||||
expect(account).toEqual({
|
||||
address: toAccAddress(validator.address),
|
||||
pubKey: encodeAminoPubkey(validator.pubkey),
|
||||
// accountNumber not set
|
||||
sequence: Long.fromNumber(validator.sequence, true),
|
||||
});
|
||||
});
|
||||
|
||||
it("returns null for non-existent address", async () => {
|
||||
pending("This fails with Error: Query failed with (1): internal");
|
||||
pendingWithoutSimapp();
|
||||
const client = await makeAuthClient(simapp.tendermintUrl);
|
||||
|
||||
const account = await client.auth.unverified.account(nonExistentAddress);
|
||||
expect(account).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
60
packages/stargate/src/queries/auth.ts
Normal file
60
packages/stargate/src/queries/auth.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { cosmos, google } from "../generated/codecimpl";
|
||||
import { QueryClient } from "./queryclient";
|
||||
import { toAccAddress, toObject } from "./utils";
|
||||
|
||||
export interface AuthExtension {
|
||||
readonly auth: {
|
||||
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
|
||||
readonly unverified: {
|
||||
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function setupAuthExtension(base: QueryClient): AuthExtension {
|
||||
// Use this service to get easy typed access to query methods
|
||||
// This cannot be used to for proof verification
|
||||
const queryService = cosmos.auth.Query.create((method: any, requestData, callback) => {
|
||||
// Parts of the path are unavailable, so we hardcode them here. See https://github.com/protobufjs/protobuf.js/issues/1229
|
||||
const path = `/cosmos.auth.Query/${method.name}`;
|
||||
base
|
||||
.queryUnverified(path, requestData)
|
||||
.then((response) => callback(null, response))
|
||||
.catch((error) => callback(error));
|
||||
});
|
||||
|
||||
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;
|
||||
const account = google.protobuf.Any.decode(responseData);
|
||||
switch (account.type_url) {
|
||||
case "/cosmos.auth.BaseAccount": {
|
||||
return toObject(cosmos.auth.BaseAccount.decode(account.value));
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported type: '${account.type_url}'`);
|
||||
}
|
||||
},
|
||||
unverified: {
|
||||
account: async (address: string) => {
|
||||
const { account } = await queryService.account({ address: toAccAddress(address) });
|
||||
if (!account) return null;
|
||||
switch (account.type_url) {
|
||||
case "/cosmos.auth.BaseAccount": {
|
||||
assert(account.value);
|
||||
return toObject(cosmos.auth.BaseAccount.decode(account.value));
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported type: '${account.type_url}'`);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -4,4 +4,5 @@ export { QueryClient } from "./queryclient";
|
||||
|
||||
// Extensions
|
||||
|
||||
export { AuthExtension, setupAuthExtension } from "./auth";
|
||||
export { BankExtension, setupBankExtension } from "./bank";
|
||||
|
||||
11
packages/stargate/types/queries/auth.d.ts
vendored
Normal file
11
packages/stargate/types/queries/auth.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import { cosmos } from "../generated/codecimpl";
|
||||
import { QueryClient } from "./queryclient";
|
||||
export interface AuthExtension {
|
||||
readonly auth: {
|
||||
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
|
||||
readonly unverified: {
|
||||
readonly account: (address: string) => Promise<cosmos.auth.IBaseAccount | null>;
|
||||
};
|
||||
};
|
||||
}
|
||||
export declare function setupAuthExtension(base: QueryClient): AuthExtension;
|
||||
1
packages/stargate/types/queries/index.d.ts
vendored
1
packages/stargate/types/queries/index.d.ts
vendored
@ -1,2 +1,3 @@
|
||||
export { QueryClient } from "./queryclient";
|
||||
export { AuthExtension, setupAuthExtension } from "./auth";
|
||||
export { BankExtension, setupBankExtension } from "./bank";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user