From f9081acd121e72bb9a479148a7f573c0e97525e0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 15 Feb 2020 11:41:42 +0100 Subject: [PATCH 1/5] Improve BlockResponse types --- packages/sdk/src/restclient.ts | 42 ++++++++++++++++++++++-------- packages/sdk/types/restclient.d.ts | 20 +++++++------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 2126cb7c..7c09e9e4 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -22,7 +22,7 @@ interface NodeInfoResponse { readonly node_info: NodeInfo; } -interface BlockMeta { +export interface BlockMeta { readonly header: { readonly height: number; readonly time: string; @@ -33,13 +33,33 @@ interface BlockMeta { }; } -interface Block { - readonly header: { - readonly height: number; - }; +export interface BlockHeader { + readonly height: number; + readonly chain_id: 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' + // }, } -interface BlocksResponse { +export interface Block { + readonly header: BlockHeader; +} + +export interface BlockResponse { readonly block_meta: BlockMeta; readonly block: Block; } @@ -119,7 +139,7 @@ interface SmartQueryResponse { type RestClientResponse = | NodeInfoResponse - | BlocksResponse + | BlockResponse | AuthAccountsResponse | TxsResponse | SearchTxsResponse @@ -209,20 +229,20 @@ export class RestClient { return responseData as NodeInfoResponse; } - public async blocksLatest(): Promise { + public async blocksLatest(): Promise { 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 { + public async blocks(height: number): Promise { 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 */ diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index 8b82fb29..28af8a9d 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -5,7 +5,7 @@ interface NodeInfo { interface NodeInfoResponse { readonly node_info: NodeInfo; } -interface BlockMeta { +export interface BlockMeta { readonly header: { readonly height: number; readonly time: string; @@ -15,12 +15,14 @@ interface BlockMeta { readonly hash: string; }; } -interface Block { - readonly header: { - readonly height: number; - }; +export interface BlockHeader { + readonly height: number; + readonly chain_id: string; } -interface BlocksResponse { +export interface Block { + readonly header: BlockHeader; +} +export interface BlockResponse { readonly block_meta: BlockMeta; readonly block: Block; } @@ -79,7 +81,7 @@ interface GetCodeResult { } declare type RestClientResponse = | NodeInfoResponse - | BlocksResponse + | BlockResponse | AuthAccountsResponse | TxsResponse | SearchTxsResponse @@ -95,8 +97,8 @@ export declare class RestClient { get(path: string): Promise; post(path: string, params: PostTxsParams): Promise; nodeInfo(): Promise; - blocksLatest(): Promise; - blocks(height: number): Promise; + blocksLatest(): Promise; + blocks(height: number): Promise; /** returns the amino-encoding of the transaction performed by the server */ encodeTx(tx: CosmosSdkTx): Promise; authAccounts(address: string): Promise; From b80d5c39fce534c3f316bdd81ed95f49500e86f0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 15 Feb 2020 11:46:59 +0100 Subject: [PATCH 2/5] Fix height type --- packages/bcp/src/cosmwasmconnection.ts | 2 +- packages/sdk/src/restclient.ts | 2 +- packages/sdk/types/restclient.d.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index ffe4540a..5b37b935 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -132,7 +132,7 @@ export class CosmWasmConnection implements BlockchainConnection { public async height(): Promise { // tslint:disable-next-line: deprecation const { block } = await this.restClient.blocksLatest(); - return block.header.height; + return parseInt(block.header.height, 10); } public async getToken(searchTicker: TokenTicker): Promise { diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 7c09e9e4..61b8bb24 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -34,7 +34,7 @@ export interface BlockMeta { } export interface BlockHeader { - readonly height: number; + readonly height: string; readonly chain_id: string; // TODO: add all of those // header: { diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index 28af8a9d..a67bc175 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -16,7 +16,7 @@ export interface BlockMeta { }; } export interface BlockHeader { - readonly height: number; + readonly height: string; readonly chain_id: string; } export interface Block { From fa78a7460cd5da18da5f2ef279eb2527418f23db Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 15 Feb 2020 15:44:12 +0100 Subject: [PATCH 3/5] Fix broken BlockResponse types --- packages/bcp/src/cosmwasmconnection.ts | 10 +++++----- packages/sdk/src/restclient.ts | 26 ++++++++++++++------------ packages/sdk/types/restclient.d.ts | 20 +++++++++----------- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 5b37b935..53d415e5 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -218,12 +218,12 @@ export class CosmWasmConnection implements BlockchainConnection { public async getBlockHeader(height: number): Promise { // tslint:disable-next-line: deprecation - const { block_meta } = await this.restClient.blocks(height); + const { block_id, block } = await this.restClient.blocks(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, }; } diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 61b8bb24..4377d315 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -22,20 +22,11 @@ interface NodeInfoResponse { readonly node_info: NodeInfo; } -export interface BlockMeta { - readonly header: { - readonly height: number; - readonly time: string; - readonly num_txs: number; - }; - readonly block_id: { - readonly hash: string; - }; -} - 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], @@ -57,10 +48,21 @@ export interface BlockHeader { export interface Block { readonly header: BlockHeader; + readonly data: { + /** Array of base64 encoded transactions */ + readonly txs: ReadonlyArray | null; + }; } export interface BlockResponse { - readonly block_meta: BlockMeta; + readonly block_id: { + readonly hash: string; + // TODO: here we also have this + // parts: { + // total: '1', + // hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931' + // } + }; readonly block: Block; } diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index a67bc175..b14fbd86 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -5,25 +5,23 @@ interface NodeInfo { interface NodeInfoResponse { readonly node_info: NodeInfo; } -export interface BlockMeta { - readonly header: { - readonly height: number; - readonly time: string; - readonly num_txs: number; - }; - readonly block_id: { - readonly hash: string; - }; -} 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 | null; + }; } export interface BlockResponse { - readonly block_meta: BlockMeta; + readonly block_id: { + readonly hash: string; + }; readonly block: Block; } interface AuthAccountsResponse { From 1de2abb8ffdf6bd0e0b1e6dc6d1aadaad7e93eb7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 15 Feb 2020 16:00:09 +0100 Subject: [PATCH 4/5] Add CosmWasmClient.getBlock --- packages/sdk/package.json | 3 ++ packages/sdk/src/cosmwasmclient.spec.ts | 44 +++++++++++++++++++++++++ packages/sdk/src/cosmwasmclient.ts | 15 ++++++++- packages/sdk/types/cosmwasmclient.d.ts | 8 ++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/sdk/package.json b/packages/sdk/package.json index fba2fbb3..a45a3bee 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -42,5 +42,8 @@ "@iov/encoding": "^2.0.2", "@iov/utils": "^2.0.2", "axios": "^0.19.0" + }, + "devDependencies": { + "readonly-date": "^1.0.0" } } diff --git a/packages/sdk/src/cosmwasmclient.spec.ts b/packages/sdk/src/cosmwasmclient.spec.ts index ce821e7c..72321a1b 100644 --- a/packages/sdk/src/cosmwasmclient.spec.ts +++ b/packages/sdk/src/cosmwasmclient.spec.ts @@ -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(); diff --git a/packages/sdk/src/cosmwasmclient.ts b/packages/sdk/src/cosmwasmclient.ts index 1079dd85..b80e86f8 100644 --- a/packages/sdk/src/cosmwasmclient.ts +++ b/packages/sdk/src/cosmwasmclient.ts @@ -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 { + if (height !== undefined) { + return this.restClient.blocks(height); + } else { + return this.restClient.blocksLatest(); + } + } + public async searchTx(query: SearchTxQuery): Promise { // TODO: we need proper pagination support function limited(originalQuery: string): string { diff --git a/packages/sdk/types/cosmwasmclient.d.ts b/packages/sdk/types/cosmwasmclient.d.ts index 88eebd4c..bd66f743 100644 --- a/packages/sdk/types/cosmwasmclient.d.ts +++ b/packages/sdk/types/cosmwasmclient.d.ts @@ -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; @@ -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; + /** + * Gets block header and meta + * + * @param height The height of the block. If undefined, the latest height is used. + */ + getBlock(height?: number): Promise; searchTx(query: SearchTxQuery): Promise; postTx(tx: Uint8Array): Promise; /** Uploads code and returns a code ID */ From 4470efbeda6869677a59123d9228b861d666b2df Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 15 Feb 2020 16:02:58 +0100 Subject: [PATCH 5/5] Use CosmWasmClient.getBlock --- packages/bcp/src/cosmwasmconnection.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 53d415e5..25122b27 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -130,8 +130,7 @@ export class CosmWasmConnection implements BlockchainConnection { } public async height(): Promise { - // tslint:disable-next-line: deprecation - const { block } = await this.restClient.blocksLatest(); + const { block } = await this.cosmWasmClient.getBlock(); return parseInt(block.header.height, 10); } @@ -217,8 +216,7 @@ export class CosmWasmConnection implements BlockchainConnection { } public async getBlockHeader(height: number): Promise { - // tslint:disable-next-line: deprecation - const { block_id, block } = await this.restClient.blocks(height); + const { block_id, block } = await this.cosmWasmClient.getBlock(height); return { id: block_id.hash as BlockId, height: parseInt(block.header.height, 10),