Add more efficient getHeight implementation

This commit is contained in:
Simon Warta 2020-03-07 18:25:21 +01:00
parent e7f60e9abd
commit a08b07f320
5 changed files with 95 additions and 22 deletions

View File

@ -4,7 +4,7 @@ import { Bech32, Encoding } from "@iov/encoding";
import { assert, sleep } from "@iov/utils";
import { ReadonlyDate } from "readonly-date";
import { Code, CosmWasmClient } from "./cosmwasmclient";
import { Code, CosmWasmClient, PrivateCosmWasmClient } from "./cosmwasmclient";
import { makeSignBytes } from "./encoding";
import { findAttribute } from "./logs";
import { Secp256k1Pen } from "./pen";
@ -57,14 +57,42 @@ describe("CosmWasmClient", () => {
});
describe("getHeight", () => {
it("works", async () => {
it("gets height via last block", async () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmdEndpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
await sleep(1_000);
const height2 = await client.getHeight();
expect(height2).toEqual(height1 + 1);
expect(blockLatestSpy).toHaveBeenCalledTimes(2);
});
it("gets height via authAccount once an address is known", async () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmdEndpoint);
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough();
const height1 = await client.getHeight();
expect(height1).toBeGreaterThan(0);
await client.getCodes(); // warm up the client
const height2 = await client.getHeight();
expect(height2).toBeGreaterThan(0);
await sleep(1_000);
const height3 = await client.getHeight();
expect(height3).toEqual(height2 + 1);
expect(blockLatestSpy).toHaveBeenCalledTimes(1);
expect(authAccountsSpy).toHaveBeenCalledTimes(2);
});
});

View File

@ -137,8 +137,15 @@ export interface Block {
readonly txs: ReadonlyArray<Uint8Array>;
}
/** Use for testing only */
export interface PrivateCosmWasmClient {
readonly restClient: RestClient;
}
export class CosmWasmClient {
protected readonly restClient: RestClient;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
public constructor(url: string, broadcastMode = BroadcastMode.Block) {
this.restClient = new RestClient(url, broadcastMode);
@ -150,10 +157,15 @@ export class CosmWasmClient {
}
public async getHeight(): Promise<number> {
// Note: this gets inefficient when blocks contain a lot of transactions since it
// requires downloading and deserializing all transactions in the block.
const latest = await this.restClient.blocksLatest();
return parseInt(latest.block.header.height, 10);
if (this.anyValidAddress) {
const { height } = await this.restClient.authAccounts(this.anyValidAddress);
return parseInt(height, 10);
} else {
// Note: this gets inefficient when blocks contain a lot of transactions since it
// requires downloading and deserializing all transactions in the block.
const latest = await this.restClient.blocksLatest();
return parseInt(latest.block.header.height, 10);
}
}
/**
@ -189,15 +201,18 @@ export class CosmWasmClient {
public async getAccount(address: string): Promise<Account | undefined> {
const account = await this.restClient.authAccounts(address);
const value = account.result.value;
return value.address === ""
? undefined
: {
address: value.address,
balance: value.coins,
pubkey: value.public_key ? decodeBech32Pubkey(value.public_key) : undefined,
accountNumber: value.account_number,
sequence: value.sequence,
};
if (value.address === "") {
return undefined;
} else {
this.anyValidAddress = value.address;
return {
address: value.address,
balance: value.coins,
pubkey: value.public_key ? decodeBech32Pubkey(value.public_key) : undefined,
accountNumber: value.account_number,
sequence: value.sequence,
};
}
}
/**
@ -283,13 +298,16 @@ export class CosmWasmClient {
public async getCodes(): Promise<readonly Code[]> {
const result = await this.restClient.listCodeInfo();
return result.map(
(entry): Code => ({
id: entry.id,
creator: entry.creator,
checksum: Encoding.toHex(Encoding.fromHex(entry.data_hash)),
source: entry.source || undefined,
builder: entry.builder || undefined,
}),
(entry): Code => {
this.anyValidAddress = entry.creator;
return {
id: entry.id,
creator: entry.creator,
checksum: Encoding.toHex(Encoding.fromHex(entry.data_hash)),
source: entry.source || undefined,
builder: entry.builder || undefined,
};
},
);
}

View File

@ -2,6 +2,7 @@ import { Sha256 } from "@iov/crypto";
import { Encoding } from "@iov/encoding";
import { assert } from "@iov/utils";
import { PrivateCosmWasmClient } from "./cosmwasmclient";
import { Secp256k1Pen } from "./pen";
import { RestClient } from "./restclient";
import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient";
@ -31,6 +32,24 @@ describe("SigningCosmWasmClient", () => {
});
});
describe("getHeight", () => {
it("always uses authAccount implementation", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const openedClient = (client as unknown) as PrivateCosmWasmClient;
const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough();
const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough();
const height = await client.getHeight();
expect(height).toBeGreaterThan(0);
expect(blockLatestSpy).toHaveBeenCalledTimes(0);
expect(authAccountsSpy).toHaveBeenCalledTimes(1);
});
});
describe("upload", () => {
it("works", async () => {
pendingWithoutWasmd();

View File

@ -111,6 +111,8 @@ export class SigningCosmWasmClient extends CosmWasmClient {
broadcastMode = BroadcastMode.Block,
) {
super(url, broadcastMode);
this.anyValidAddress = senderAddress;
this.senderAddress = senderAddress;
this.signCallback = signCallback;
this.fees = { ...defaultFees, ...(customFees || {}) };

View File

@ -103,8 +103,14 @@ export interface Block {
/** Array of raw transactions */
readonly txs: ReadonlyArray<Uint8Array>;
}
/** Use for testing only */
export interface PrivateCosmWasmClient {
readonly restClient: RestClient;
}
export declare class CosmWasmClient {
protected readonly restClient: RestClient;
/** Any address the chain considers valid (valid bech32 with proper prefix) */
protected anyValidAddress: string | undefined;
constructor(url: string, broadcastMode?: BroadcastMode);
chainId(): Promise<string>;
getHeight(): Promise<number>;