Merge pull request #91 from confio/getBlocks

Add CosmWasmClient.getBlock
This commit is contained in:
merge-when-green[bot] 2020-02-16 09:01:38 +00:00 committed by GitHub
commit 4b56ad6fe7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 133 additions and 47 deletions

View File

@ -130,9 +130,8 @@ export class CosmWasmConnection implements BlockchainConnection {
}
public async height(): Promise<number> {
// tslint:disable-next-line: deprecation
const { block } = await this.restClient.blocksLatest();
return block.header.height;
const { block } = await this.cosmWasmClient.getBlock();
return parseInt(block.header.height, 10);
}
public async getToken(searchTicker: TokenTicker): Promise<Token | undefined> {
@ -217,13 +216,12 @@ export class CosmWasmConnection implements BlockchainConnection {
}
public async getBlockHeader(height: number): Promise<BlockHeader> {
// tslint:disable-next-line: deprecation
const { block_meta } = await this.restClient.blocks(height);
const { block_id, block } = await this.cosmWasmClient.getBlock(height);
return {
id: block_meta.block_id.hash as BlockId,
height: block_meta.header.height,
time: new ReadonlyDate(block_meta.header.time),
transactionCount: block_meta.header.num_txs,
id: block_id.hash as BlockId,
height: parseInt(block.header.height, 10),
time: new ReadonlyDate(block.header.time),
transactionCount: block.data.txs?.length || 0,
};
}

View File

@ -42,5 +42,8 @@
"@iov/encoding": "^2.0.2",
"@iov/utils": "^2.0.2",
"axios": "^0.19.0"
},
"devDependencies": {
"readonly-date": "^1.0.0"
}
}

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/camelcase */
import { Bech32, Encoding } from "@iov/encoding";
import { assert, sleep } from "@iov/utils";
import { ReadonlyDate } from "readonly-date";
import { CosmWasmClient } from "./cosmwasmclient";
import { makeSignBytes, marshalTx } from "./encoding";
@ -74,6 +75,49 @@ describe("CosmWasmClient", () => {
});
});
describe("getBlock", () => {
it("works for latest block", async () => {
pendingWithoutCosmos();
const client = CosmWasmClient.makeReadOnly(httpUrl);
const response = await client.getBlock();
// id
expect(response.block_id.hash).toMatch(/^[0-9A-F]{64}$/);
// header
expect(parseInt(response.block.header.height, 10)).toBeGreaterThanOrEqual(1);
expect(response.block.header.chain_id).toEqual(await client.chainId());
expect(new ReadonlyDate(response.block.header.time).getTime()).toBeLessThan(ReadonlyDate.now());
expect(new ReadonlyDate(response.block.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
// data
expect(response.block.data.txs === null || Array.isArray(response.block.data.txs)).toEqual(true);
});
it("works for block by height", async () => {
pendingWithoutCosmos();
const client = CosmWasmClient.makeReadOnly(httpUrl);
const height = parseInt((await client.getBlock()).block.header.height, 10);
const response = await client.getBlock(height - 1);
// id
expect(response.block_id.hash).toMatch(/^[0-9A-F]{64}$/);
// header
expect(response.block.header.height).toEqual(`${height - 1}`);
expect(response.block.header.chain_id).toEqual(await client.chainId());
expect(new ReadonlyDate(response.block.header.time).getTime()).toBeLessThan(ReadonlyDate.now());
expect(new ReadonlyDate(response.block.header.time).getTime()).toBeGreaterThanOrEqual(
ReadonlyDate.now() - 5_000,
);
// data
expect(response.block.data.txs === null || Array.isArray(response.block.data.txs)).toEqual(true);
});
});
describe("getIdentifier", () => {
it("works", async () => {
pendingWithoutCosmos();

View File

@ -3,7 +3,7 @@ import { Encoding } from "@iov/encoding";
import { makeSignBytes, marshalTx } from "./encoding";
import { findAttribute, Log, parseLogs } from "./logs";
import { RestClient, TxsResponse } from "./restclient";
import { BlockResponse, RestClient, TxsResponse } from "./restclient";
import {
Coin,
CosmosSdkTx,
@ -157,6 +157,19 @@ export class CosmWasmClient {
};
}
/**
* Gets block header and meta
*
* @param height The height of the block. If undefined, the latest height is used.
*/
public async getBlock(height?: number): Promise<BlockResponse> {
if (height !== undefined) {
return this.restClient.blocks(height);
} else {
return this.restClient.blocksLatest();
}
}
public async searchTx(query: SearchTxQuery): Promise<readonly TxsResponse[]> {
// TODO: we need proper pagination support
function limited(originalQuery: string): string {

View File

@ -22,25 +22,47 @@ interface NodeInfoResponse {
readonly node_info: NodeInfo;
}
interface BlockMeta {
readonly header: {
readonly height: number;
readonly time: string;
readonly num_txs: number;
export interface BlockHeader {
readonly height: string;
readonly chain_id: string;
/** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */
readonly time: string;
// TODO: add all of those
// header: {
// version: [Object],
// chain_id: 'testing',
// height: '41121',
// time: '2020-02-15T10:39:10.4696305Z',
// last_block_id: [Object],
// last_commit_hash: '9C68EDA02AEB5F6A76AA03F7F7E6834D73424A8906FE5A79B04D310C2DB5EFFC',
// data_hash: '',
// validators_hash: '4412A0B61BAC7D1EEDA531F3FBA8E90BBB3DDF6CCA85B28CA1D8300818F0E7EA',
// next_validators_hash: '4412A0B61BAC7D1EEDA531F3FBA8E90BBB3DDF6CCA85B28CA1D8300818F0E7EA',
// consensus_hash: '048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F',
// app_hash: 'DD58B3B4AB1031ECB0CED45C017B57EE2E6E22FA7F0ECA355268CC205C6A1A64',
// last_results_hash: '',
// evidence_hash: '',
// proposer_address: '3FBF50B72FE062495F150AEB78D1981E7DAEBE60'
// },
}
export interface Block {
readonly header: BlockHeader;
readonly data: {
/** Array of base64 encoded transactions */
readonly txs: ReadonlyArray<string> | null;
};
}
export interface BlockResponse {
readonly block_id: {
readonly hash: string;
// TODO: here we also have this
// parts: {
// total: '1',
// hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931'
// }
};
}
interface Block {
readonly header: {
readonly height: number;
};
}
interface BlocksResponse {
readonly block_meta: BlockMeta;
readonly block: Block;
}
@ -119,7 +141,7 @@ interface SmartQueryResponse {
type RestClientResponse =
| NodeInfoResponse
| BlocksResponse
| BlockResponse
| AuthAccountsResponse
| TxsResponse
| SearchTxsResponse
@ -209,20 +231,20 @@ export class RestClient {
return responseData as NodeInfoResponse;
}
public async blocksLatest(): Promise<BlocksResponse> {
public async blocksLatest(): Promise<BlockResponse> {
const responseData = await this.get("/blocks/latest");
if (!(responseData as any).block) {
throw new Error("Unexpected response data format");
}
return responseData as BlocksResponse;
return responseData as BlockResponse;
}
public async blocks(height: number): Promise<BlocksResponse> {
public async blocks(height: number): Promise<BlockResponse> {
const responseData = await this.get(`/blocks/${height}`);
if (!(responseData as any).block) {
throw new Error("Unexpected response data format");
}
return responseData as BlocksResponse;
return responseData as BlockResponse;
}
/** returns the amino-encoding of the transaction performed by the server */

View File

@ -1,5 +1,5 @@
import { Log } from "./logs";
import { TxsResponse } from "./restclient";
import { BlockResponse, TxsResponse } from "./restclient";
import { Coin, CosmosSdkTx, StdSignature } from "./types";
export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>;
@ -46,6 +46,12 @@ export declare class CosmWasmClient {
* @param address returns data for this address. When unset, the client's sender adddress is used.
*/
getNonce(address?: string): Promise<GetNonceResult>;
/**
* Gets block header and meta
*
* @param height The height of the block. If undefined, the latest height is used.
*/
getBlock(height?: number): Promise<BlockResponse>;
searchTx(query: SearchTxQuery): Promise<readonly TxsResponse[]>;
postTx(tx: Uint8Array): Promise<PostTxResult>;
/** Uploads code and returns a code ID */

View File

@ -5,23 +5,23 @@ interface NodeInfo {
interface NodeInfoResponse {
readonly node_info: NodeInfo;
}
interface BlockMeta {
readonly header: {
readonly height: number;
readonly time: string;
readonly num_txs: number;
export interface BlockHeader {
readonly height: string;
readonly chain_id: string;
/** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */
readonly time: string;
}
export interface Block {
readonly header: BlockHeader;
readonly data: {
/** Array of base64 encoded transactions */
readonly txs: ReadonlyArray<string> | null;
};
}
export interface BlockResponse {
readonly block_id: {
readonly hash: string;
};
}
interface Block {
readonly header: {
readonly height: number;
};
}
interface BlocksResponse {
readonly block_meta: BlockMeta;
readonly block: Block;
}
interface AuthAccountsResponse {
@ -79,7 +79,7 @@ interface GetCodeResult {
}
declare type RestClientResponse =
| NodeInfoResponse
| BlocksResponse
| BlockResponse
| AuthAccountsResponse
| TxsResponse
| SearchTxsResponse
@ -95,8 +95,8 @@ export declare class RestClient {
get(path: string): Promise<RestClientResponse>;
post(path: string, params: PostTxsParams): Promise<RestClientResponse>;
nodeInfo(): Promise<NodeInfoResponse>;
blocksLatest(): Promise<BlocksResponse>;
blocks(height: number): Promise<BlocksResponse>;
blocksLatest(): Promise<BlockResponse>;
blocks(height: number): Promise<BlockResponse>;
/** returns the amino-encoding of the transaction performed by the server */
encodeTx(tx: CosmosSdkTx): Promise<Uint8Array>;
authAccounts(address: string): Promise<AuthAccountsResponse>;