From 5f694f2ed6b75cb076b1e352a3dcb3c3b5a5015e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 26 May 2021 13:45:02 +0200 Subject: [PATCH 1/3] Upgrade Tendermint version in tests to 0.34.10 and adapt code --- .../src/legacy/adaptors/index.ts | 18 +++++------------- .../tendermint-rpc/src/legacy/client.spec.ts | 2 +- .../tendermint34/tendermint34client.spec.ts | 2 +- packages/tendermint-rpc/src/testutil.spec.ts | 9 +++++---- scripts/tendermint/all_start.sh | 2 +- scripts/tendermint/all_stop.sh | 2 +- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/packages/tendermint-rpc/src/legacy/adaptors/index.ts b/packages/tendermint-rpc/src/legacy/adaptors/index.ts index 51223aba..7097e36a 100644 --- a/packages/tendermint-rpc/src/legacy/adaptors/index.ts +++ b/packages/tendermint-rpc/src/legacy/adaptors/index.ts @@ -28,23 +28,15 @@ export const adaptor33 = v0_33; */ export const adaptor34 = v0_33; // With this alias we can swap out the implementation without affecting caller code. -const hashes = { - v0_34: [ - "ca2c9df", // v0.34.0-rc6 - "182fa32", // v0.34.0 - ], -}; - /** * Returns an Adaptor implementation for a given tendermint version. * Throws when version is not supported. * * @param version full Tendermint version string, e.g. "0.20.1" */ -export function adaptorForVersion(version: string): Adaptor { - if (version.startsWith("0.33.") || version.startsWith("0.34.") || hashes.v0_34.includes(version)) { - return v0_33; - } else { - throw new Error(`Unsupported tendermint version: ${version}`); - } +export function adaptorForVersion(_version: string): Adaptor { + // Note: In some cases, Tendermint 0.34 returns an empty version value. + // This supports 0.33 and 0.34 now, no matter which version you provide. + // Very soon this function becomes obsolete (https://github.com/cosmos/cosmjs/issues/789). + return v0_33; } diff --git a/packages/tendermint-rpc/src/legacy/client.spec.ts b/packages/tendermint-rpc/src/legacy/client.spec.ts index a3c290a5..eda05c4a 100644 --- a/packages/tendermint-rpc/src/legacy/client.spec.ts +++ b/packages/tendermint-rpc/src/legacy/client.spec.ts @@ -167,7 +167,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, expecte const status = await client.status(); // node info - expect(status.nodeInfo.version).toEqual(expected.version); + expect(status.nodeInfo.version).toMatch(expected.version); expect(status.nodeInfo.protocolVersion).toEqual({ p2p: expected.p2pVersion, block: expected.blockVersion, diff --git a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts index 1f5b1666..2c33d216 100644 --- a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts +++ b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts @@ -166,7 +166,7 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues) const status = await client.status(); // node info - expect(status.nodeInfo.version).toEqual(expected.version); + expect(status.nodeInfo.version).toMatch(expected.version); expect(status.nodeInfo.protocolVersion).toEqual({ p2p: expected.p2pVersion, block: expected.blockVersion, diff --git a/packages/tendermint-rpc/src/testutil.spec.ts b/packages/tendermint-rpc/src/testutil.spec.ts index c7315bf5..353dd26f 100644 --- a/packages/tendermint-rpc/src/testutil.spec.ts +++ b/packages/tendermint-rpc/src/testutil.spec.ts @@ -1,9 +1,12 @@ import { toAscii } from "@cosmjs/encoding"; import { sleep } from "@cosmjs/utils"; +export const chainIdMatcher = /^[-a-zA-Z0-9]{3,30}$/; +export const anyMatcher = /^.*$/; // Any string, including empty. Does not do more than a type check. + export interface ExpectedValues { /** The Tendermint version as reported by Tendermint itself */ - readonly version: string; + readonly version: string | RegExp; readonly appCreator: string; readonly p2pVersion: number; readonly blockVersion: number; @@ -49,7 +52,7 @@ export const tendermintInstances: readonly TendermintInstance[] = [ version: "0.34.x", blockTime: 500, expected: { - version: "182fa32", // srsly? + version: anyMatcher, appCreator: "Cosmoshi Netowoko", p2pVersion: 8, blockVersion: 11, @@ -60,8 +63,6 @@ export const tendermintInstances: readonly TendermintInstance[] = [ export const defaultInstance: TendermintInstance = tendermintInstances[0]; -export const chainIdMatcher = /^[-a-zA-Z0-9]{3,30}$/; - export function tendermintEnabled(): boolean { return !!process.env.TENDERMINT_ENABLED; } diff --git a/scripts/tendermint/all_start.sh b/scripts/tendermint/all_start.sh index bbe27ca1..37a87fa8 100755 --- a/scripts/tendermint/all_start.sh +++ b/scripts/tendermint/all_start.sh @@ -5,7 +5,7 @@ command -v shellcheck >/dev/null && shellcheck "$0" # Find latest patch releases at https://hub.docker.com/r/tendermint/tendermint/tags/ declare -a TM_VERSIONS TM_VERSIONS[33]=v0.33.8 -TM_VERSIONS[34]=v0.34.0 +TM_VERSIONS[34]=v0.34.10 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/scripts/tendermint/all_stop.sh b/scripts/tendermint/all_stop.sh index a2ccd3e2..e77084df 100755 --- a/scripts/tendermint/all_stop.sh +++ b/scripts/tendermint/all_stop.sh @@ -4,7 +4,7 @@ command -v shellcheck >/dev/null && shellcheck "$0" declare -a TM_VERSIONS TM_VERSIONS[33]=v0.33.8 -TM_VERSIONS[34]=v0.34.0 +TM_VERSIONS[34]=v0.34.10 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" From 65db62d3eef293143a0e99e2ae39e59899ded99b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 23 Jun 2021 13:24:16 +0200 Subject: [PATCH 2/3] Backport block search --- CHANGELOG.md | 8 +++ .../src/tendermint34/adaptor.ts | 2 + .../tendermint34/adaptors/v0-34/requests.ts | 19 ++++++ .../tendermint34/adaptors/v0-34/responses.ts | 16 +++++ .../tendermint-rpc/src/tendermint34/index.ts | 3 + .../src/tendermint34/requests.ts | 14 +++++ .../src/tendermint34/responses.ts | 5 ++ .../tendermint34/tendermint34client.spec.ts | 63 +++++++++++++++++++ .../src/tendermint34/tendermint34client.ts | 47 ++++++++++++++ 9 files changed, 177 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a7f4e2b..c6bdbb50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this project adheres to ## [Unreleased] +### Added + +- @cosmjs/tendermint-rpc: `Tendermint34Client.blockSearch` and + `Tendermint34Client.blockSearchAll` were added to allow searching blocks in + Tendermint 0.34.9+ backends. This is a backport of [#815]. + +[#815]: https://github.com/cosmos/cosmjs/pull/815 + ## [0.25.4] - 2021-05-31 ### Fixed diff --git a/packages/tendermint-rpc/src/tendermint34/adaptor.ts b/packages/tendermint-rpc/src/tendermint34/adaptor.ts index 7248942d..44c06917 100644 --- a/packages/tendermint-rpc/src/tendermint34/adaptor.ts +++ b/packages/tendermint-rpc/src/tendermint34/adaptor.ts @@ -23,6 +23,7 @@ export interface Params { readonly encodeBlock: (req: requests.BlockRequest) => JsonRpcRequest; readonly encodeBlockchain: (req: requests.BlockchainRequest) => JsonRpcRequest; readonly encodeBlockResults: (req: requests.BlockResultsRequest) => JsonRpcRequest; + readonly encodeBlockSearch: (req: requests.BlockSearchRequest) => JsonRpcRequest; readonly encodeBroadcastTx: (req: requests.BroadcastTxRequest) => JsonRpcRequest; readonly encodeCommit: (req: requests.CommitRequest) => JsonRpcRequest; readonly encodeGenesis: (req: requests.GenesisRequest) => JsonRpcRequest; @@ -39,6 +40,7 @@ export interface Responses { readonly decodeAbciQuery: (response: JsonRpcSuccessResponse) => responses.AbciQueryResponse; readonly decodeBlock: (response: JsonRpcSuccessResponse) => responses.BlockResponse; readonly decodeBlockResults: (response: JsonRpcSuccessResponse) => responses.BlockResultsResponse; + readonly decodeBlockSearch: (response: JsonRpcSuccessResponse) => responses.BlockSearchResponse; readonly decodeBlockchain: (response: JsonRpcSuccessResponse) => responses.BlockchainResponse; readonly decodeBroadcastTxSync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxSyncResponse; readonly decodeBroadcastTxAsync: (response: JsonRpcSuccessResponse) => responses.BroadcastTxAsyncResponse; diff --git a/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/requests.ts b/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/requests.ts index 54436f7a..1dc11c4c 100644 --- a/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/requests.ts +++ b/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/requests.ts @@ -30,6 +30,21 @@ function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): }; } +interface RpcBlockSearchParams { + readonly query: string; + readonly page?: string; + readonly per_page?: string; + readonly order_by?: string; +} +function encodeBlockSearchParams(params: requests.BlockSearchParams): RpcBlockSearchParams { + return { + query: params.query, + page: may(Integer.encode, params.page), + per_page: may(Integer.encode, params.per_page), + order_by: params.order_by, + }; +} + interface RpcAbciQueryParams { readonly path: string; /** hex encoded */ @@ -118,6 +133,10 @@ export class Params { return createJsonRpcRequest(req.method, encodeHeightParam(req.params)); } + public static encodeBlockSearch(req: requests.BlockSearchRequest): JsonRpcRequest { + return createJsonRpcRequest(req.method, encodeBlockSearchParams(req.params)); + } + public static encodeBroadcastTx(req: requests.BroadcastTxRequest): JsonRpcRequest { return createJsonRpcRequest(req.method, encodeBroadcastTxParams(req.params)); } diff --git a/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/responses.ts b/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/responses.ts index 5386c7e1..7f5ffa0e 100644 --- a/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/responses.ts +++ b/packages/tendermint-rpc/src/tendermint34/adaptors/v0-34/responses.ts @@ -783,6 +783,18 @@ function decodeBlockResponse(data: RpcBlockResponse): responses.BlockResponse { }; } +interface RpcBlockSearchResponse { + readonly blocks: readonly RpcBlockResponse[]; + readonly total_count: string; +} + +function decodeBlockSearch(data: RpcBlockSearchResponse): responses.BlockSearchResponse { + return { + totalCount: Integer.parse(assertNotEmpty(data.total_count)), + blocks: assertArray(data.blocks).map(decodeBlockResponse), + }; +} + export class Responses { public static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse { return decodeAbciInfo(assertObject((response.result as AbciInfoResult).response)); @@ -800,6 +812,10 @@ export class Responses { return decodeBlockResults(response.result as RpcBlockResultsResponse); } + public static decodeBlockSearch(response: JsonRpcSuccessResponse): responses.BlockSearchResponse { + return decodeBlockSearch(response.result as RpcBlockSearchResponse); + } + public static decodeBlockchain(response: JsonRpcSuccessResponse): responses.BlockchainResponse { return decodeBlockchain(response.result as RpcBlockchainResponse); } diff --git a/packages/tendermint-rpc/src/tendermint34/index.ts b/packages/tendermint-rpc/src/tendermint34/index.ts index f67990c2..324bae5a 100644 --- a/packages/tendermint-rpc/src/tendermint34/index.ts +++ b/packages/tendermint-rpc/src/tendermint34/index.ts @@ -8,6 +8,8 @@ export { AbciQueryRequest, BlockRequest, BlockchainRequest, + BlockSearchParams, + BlockSearchRequest, BlockResultsRequest, BroadcastTxRequest, BroadcastTxParams, @@ -38,6 +40,7 @@ export { BlockParams, BlockResponse, BlockResultsResponse, + BlockSearchResponse, BroadcastTxAsyncResponse, BroadcastTxCommitResponse, broadcastTxCommitSuccess, diff --git a/packages/tendermint-rpc/src/tendermint34/requests.ts b/packages/tendermint-rpc/src/tendermint34/requests.ts index d985b8e6..1f8f4888 100644 --- a/packages/tendermint-rpc/src/tendermint34/requests.ts +++ b/packages/tendermint-rpc/src/tendermint34/requests.ts @@ -12,6 +12,7 @@ export enum Method { /** Get block headers for minHeight <= height <= maxHeight. */ Blockchain = "blockchain", BlockResults = "block_results", + BlockSearch = "block_search", BroadcastTxAsync = "broadcast_tx_async", BroadcastTxSync = "broadcast_tx_sync", BroadcastTxCommit = "broadcast_tx_commit", @@ -30,6 +31,7 @@ export type Request = | AbciInfoRequest | AbciQueryRequest | BlockRequest + | BlockSearchRequest | BlockchainRequest | BlockResultsRequest | BroadcastTxRequest @@ -97,6 +99,18 @@ export interface BlockResultsRequest { }; } +export interface BlockSearchRequest { + readonly method: Method.BlockSearch; + readonly params: BlockSearchParams; +} + +export interface BlockSearchParams { + readonly query: string; + readonly page?: number; + readonly per_page?: number; + readonly order_by?: string; +} + export interface BroadcastTxRequest { readonly method: Method.BroadcastTxAsync | Method.BroadcastTxSync | Method.BroadcastTxCommit; readonly params: BroadcastTxParams; diff --git a/packages/tendermint-rpc/src/tendermint34/responses.ts b/packages/tendermint-rpc/src/tendermint34/responses.ts index ba7d3970..b71c4826 100644 --- a/packages/tendermint-rpc/src/tendermint34/responses.ts +++ b/packages/tendermint-rpc/src/tendermint34/responses.ts @@ -60,6 +60,11 @@ export interface BlockResultsResponse { readonly endBlockEvents: readonly Event[]; } +export interface BlockSearchResponse { + readonly blocks: readonly BlockResponse[]; + readonly totalCount: number; +} + export interface BlockchainResponse { readonly lastHeight: number; readonly blockMetas: readonly BlockMeta[]; diff --git a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts index 2c33d216..006b33a3 100644 --- a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts +++ b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts @@ -204,6 +204,69 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues) }); }); + describe("blockSearch", () => { + beforeAll(async () => { + if (tendermintEnabled()) { + const client = await Tendermint34Client.create(rpcFactory()); + + // eslint-disable-next-line no-inner-declarations + async function sendTx(): Promise { + const tx = buildKvTx(randomString(), randomString()); + + const txRes = await client.broadcastTxCommit({ tx: tx }); + expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true); + expect(txRes.height).toBeTruthy(); + expect(txRes.hash.length).not.toEqual(0); + } + + // send 3 txs + await sendTx(); + await sendTx(); + await sendTx(); + + client.disconnect(); + + await tendermintSearchIndexUpdated(); + } + }); + + it("can paginate over blockSearch results", async () => { + pendingWithoutTendermint(); + const client = await Tendermint34Client.create(rpcFactory()); + + const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" }); + + // expect one page of results + const s1 = await client.blockSearch({ query: query, page: 1, per_page: 2 }); + expect(s1.totalCount).toEqual(3); + expect(s1.blocks.length).toEqual(2); + + // second page + const s2 = await client.blockSearch({ query: query, page: 2, per_page: 2 }); + expect(s2.totalCount).toEqual(3); + expect(s2.blocks.length).toEqual(1); + + client.disconnect(); + }); + + it("can get all search results in one call", async () => { + pendingWithoutTendermint(); + const client = await Tendermint34Client.create(rpcFactory()); + + const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" }); + + const sall = await client.blockSearchAll({ query: query, per_page: 2 }); + expect(sall.totalCount).toEqual(3); + expect(sall.blocks.length).toEqual(3); + // make sure there are in order from lowest to highest height + const [b1, b2, b3] = sall.blocks; + expect(b2.block.header.height).toEqual(b1.block.header.height + 1); + expect(b3.block.header.height).toEqual(b2.block.header.height + 1); + + client.disconnect(); + }); + }); + describe("blockchain", () => { it("returns latest in descending order by default", async () => { pendingWithoutTendermint(); diff --git a/packages/tendermint-rpc/src/tendermint34/tendermint34client.ts b/packages/tendermint-rpc/src/tendermint34/tendermint34client.ts index 42d20c4a..f528c2dd 100644 --- a/packages/tendermint-rpc/src/tendermint34/tendermint34client.ts +++ b/packages/tendermint-rpc/src/tendermint34/tendermint34client.ts @@ -98,6 +98,53 @@ export class Tendermint34Client { return this.doCall(query, this.p.encodeBlockResults, this.r.decodeBlockResults); } + /** + * Search for events that are in a block. + * + * NOTE + * This method will error on any node that is running a Tendermint version lower than 0.34.9. + * + * @see https://docs.tendermint.com/master/rpc/#/Info/block_search + */ + public async blockSearch(params: requests.BlockSearchParams): Promise { + const query: requests.BlockSearchRequest = { params: params, method: requests.Method.BlockSearch }; + const resp = await this.doCall(query, this.p.encodeBlockSearch, this.r.decodeBlockSearch); + return { + ...resp, + // make sure we sort by height, as tendermint may be sorting by string value of the height + blocks: [...resp.blocks].sort((a, b) => a.block.header.height - b.block.header.height), + }; + } + + // this should paginate through all blockSearch options to ensure it returns all results. + // starts with page 1 or whatever was provided (eg. to start on page 7) + // + // NOTE + // This method will error on any node that is running a Tendermint version lower than 0.34.9. + public async blockSearchAll(params: requests.BlockSearchParams): Promise { + let page = params.page || 1; + const blocks: responses.BlockResponse[] = []; + let done = false; + + while (!done) { + const resp = await this.blockSearch({ ...params, page: page }); + blocks.push(...resp.blocks); + if (blocks.length < resp.totalCount) { + page++; + } else { + done = true; + } + } + // make sure we sort by height, as tendermint may be sorting by string value of the height + // and the earlier items may be in a higher page than the later items + blocks.sort((a, b) => a.block.header.height - b.block.header.height); + + return { + totalCount: blocks.length, + blocks: blocks, + }; + } + /** * Queries block headers filtered by minHeight <= height <= maxHeight. * From 272dee36326a8cc6c4c82d8dc524444683ac69fa Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 23 Jun 2021 13:35:43 +0200 Subject: [PATCH 3/3] Add note on decoding block height 1 --- CHANGELOG.md | 4 +++- README.md | 7 +++++++ .../src/tendermint34/tendermint34client.spec.ts | 8 ++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6bdbb50..206bc4cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ and this project adheres to - @cosmjs/tendermint-rpc: `Tendermint34Client.blockSearch` and `Tendermint34Client.blockSearchAll` were added to allow searching blocks in - Tendermint 0.34.9+ backends. This is a backport of [#815]. + Tendermint 0.34.9+ backends. This is a backport of [#815]. Note: Decoding + blocks of height 1 is unsupported. This is fixed in [#815] and will be + released as part of CosmJS 0.26. [#815]: https://github.com/cosmos/cosmjs/pull/815 diff --git a/README.md b/README.md index 20513ba4..a9886103 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,13 @@ discussion please reach out to the team. ## Known limitations +### 0.25 + +1. Decoding blocks of height 1 is unsupported. This is fixed in [#815] and will + be released as part of CosmJS 0.26. + +[#815]: https://github.com/cosmos/cosmjs/pull/815 + ### 0.24 1. `AuthExtension` and all higher level Stargate clients only support diff --git a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts index 006b33a3..a3158348 100644 --- a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts +++ b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts @@ -234,7 +234,9 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues) pendingWithoutTendermint(); const client = await Tendermint34Client.create(rpcFactory()); - const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" }); + // Note: Block height 1 is unsupported. This is fixed in https://github.com/cosmos/cosmjs/pull/815 + // and will be released with CosmJS 0.26. + const query = buildQuery({ raw: "block.height >= 2 AND block.height <= 4" }); // expect one page of results const s1 = await client.blockSearch({ query: query, page: 1, per_page: 2 }); @@ -253,7 +255,9 @@ function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues) pendingWithoutTendermint(); const client = await Tendermint34Client.create(rpcFactory()); - const query = buildQuery({ raw: "block.height >= 1 AND block.height <= 3" }); + // Note: Block height 1 is unsupported. This is fixed in https://github.com/cosmos/cosmjs/pull/815 + // and will be released with CosmJS 0.26. + const query = buildQuery({ raw: "block.height >= 2 AND block.height <= 4" }); const sall = await client.blockSearchAll({ query: query, per_page: 2 }); expect(sall.totalCount).toEqual(3);