Create AuthExtension

This commit is contained in:
Simon Warta 2020-08-13 13:45:31 +02:00
parent 5ccc71c261
commit 4003f5eb65
5 changed files with 169 additions and 0 deletions

View 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();
});
});
});
});

View 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}'`);
}
},
},
},
};
}

View File

@ -4,4 +4,5 @@ export { QueryClient } from "./queryclient";
// Extensions
export { AuthExtension, setupAuthExtension } from "./auth";
export { BankExtension, setupBankExtension } from "./bank";

View 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;

View File

@ -1,2 +1,3 @@
export { QueryClient } from "./queryclient";
export { AuthExtension, setupAuthExtension } from "./auth";
export { BankExtension, setupBankExtension } from "./bank";