From 1cc4fbe032a0d2792a6f7f93739e61f0ef27c03b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Wed, 1 Mar 2023 17:57:05 +0100 Subject: [PATCH] Remove Tendermint 0.35 client --- CHANGELOG.md | 4 +- packages/tendermint-rpc/src/index.ts | 4 - .../src/tendermint35/adaptor/index.ts | 13 - .../src/tendermint35/adaptor/requests.ts | 185 ---- .../tendermint35/adaptor/responses.spec.ts | 124 --- .../src/tendermint35/adaptor/responses.ts | 915 ------------------ .../src/tendermint35/adaptor/types.ts | 62 -- .../src/tendermint35/encodings.spec.ts | 97 -- .../src/tendermint35/encodings.ts | 198 ---- .../src/tendermint35/hasher.spec.ts | 91 -- .../tendermint-rpc/src/tendermint35/hasher.ts | 80 -- .../tendermint-rpc/src/tendermint35/index.ts | 79 -- .../src/tendermint35/requests.spec.ts | 41 - .../src/tendermint35/requests.ts | 208 ---- .../src/tendermint35/responses.ts | 389 -------- .../tendermint35/tendermint35client.spec.ts | 866 ----------------- .../src/tendermint35/tendermint35client.ts | 361 ------- packages/tendermint-rpc/src/testutil.spec.ts | 13 - scripts/tendermint/all_start.sh | 2 - scripts/tendermint/all_stop.sh | 2 +- 20 files changed, 4 insertions(+), 3730 deletions(-) delete mode 100644 packages/tendermint-rpc/src/tendermint35/adaptor/index.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/adaptor/requests.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/adaptor/responses.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/adaptor/responses.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/adaptor/types.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/encodings.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/encodings.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/hasher.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/hasher.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/index.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/requests.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/requests.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/responses.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/tendermint35client.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint35/tendermint35client.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ca36df49..3596ebde46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,9 +32,11 @@ and this project adheres to - @cosmjs/stargate: Add `granteeGrants` and `granterGrants` queries to `AuthzExtension` ([#1308]). -- @cosmjs/tendermint-rpc: Add `Tendermint37Client` +- @cosmjs/tendermint-rpc: Add new `Tendermint37Client` and remove unused + `Tendermint35Client` ([#1376]). [#1308]: https://github.com/cosmos/cosmjs/pull/1308 +[#1376]: https://github.com/cosmos/cosmjs/pull/1376 ## [0.29.5] - 2022-12-07 diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index 7849709a66..757012a3b5 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -95,10 +95,6 @@ export { } from "./tendermint34"; export * as tendermint34 from "./tendermint34"; export { Tendermint34Client } from "./tendermint34"; -// Tendermint 0.35 support is not public. The implementation may break or be removed at any point in time. -// See https://github.com/cosmos/cosmjs/issues/1225 for more context. -// export * as tendermint35 from "./tendermint35"; -// export { Tendermint35Client } from "./tendermint35"; export * as tendermint37 from "./tendermint37"; export { Tendermint37Client } from "./tendermint37"; export { diff --git a/packages/tendermint-rpc/src/tendermint35/adaptor/index.ts b/packages/tendermint-rpc/src/tendermint35/adaptor/index.ts deleted file mode 100644 index 9f5061ff84..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/adaptor/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { hashBlock, hashTx } from "../hasher"; -import { Params } from "./requests"; -import { Responses } from "./responses"; -import { Adaptor } from "./types"; - -export { Decoder, Encoder, Params, Responses } from "./types"; - -export const adaptor35: Adaptor = { - params: Params, - responses: Responses, - hashTx: hashTx, - hashBlock: hashBlock, -}; diff --git a/packages/tendermint-rpc/src/tendermint35/adaptor/requests.ts b/packages/tendermint-rpc/src/tendermint35/adaptor/requests.ts deleted file mode 100644 index 52a06a2932..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/adaptor/requests.ts +++ /dev/null @@ -1,185 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { toBase64, toHex } from "@cosmjs/encoding"; -import { JsonRpcRequest } from "@cosmjs/json-rpc"; - -import { smallIntToApi } from "../../inthelpers"; -import { createJsonRpcRequest } from "../../jsonrpc"; -import { assertNotEmpty, may } from "../encodings"; -import * as requests from "../requests"; - -interface HeightParam { - readonly height?: number; -} -interface RpcHeightParam { - readonly height?: string; -} -function encodeHeightParam(param: HeightParam): RpcHeightParam { - return { - height: may(smallIntToApi, param.height), - }; -} - -interface RpcBlockchainRequestParams { - readonly minHeight?: string; - readonly maxHeight?: string; -} - -function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams { - return { - minHeight: may(smallIntToApi, param.minHeight), - maxHeight: may(smallIntToApi, param.maxHeight), - }; -} - -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(smallIntToApi, params.page), - per_page: may(smallIntToApi, params.per_page), - order_by: params.order_by, - }; -} - -interface RpcAbciQueryParams { - readonly path: string; - /** hex encoded */ - readonly data: string; - readonly height?: string; - readonly prove?: boolean; -} - -function encodeAbciQueryParams(params: requests.AbciQueryParams): RpcAbciQueryParams { - return { - path: assertNotEmpty(params.path), - data: toHex(params.data), - height: may(smallIntToApi, params.height), - prove: params.prove, - }; -} - -interface RpcBroadcastTxParams { - /** base64 encoded */ - readonly tx: string; -} -function encodeBroadcastTxParams(params: requests.BroadcastTxParams): RpcBroadcastTxParams { - return { - tx: toBase64(assertNotEmpty(params.tx)), - }; -} - -interface RpcTxParams { - /** hex encoded */ - readonly hash: string; - readonly prove?: boolean; -} -function encodeTxParams(params: requests.TxParams): RpcTxParams { - return { - hash: toHex(assertNotEmpty(params.hash)), - prove: params.prove, - }; -} - -interface RpcTxSearchParams { - readonly query: string; - readonly prove?: boolean; - readonly page?: string; - readonly per_page?: string; - readonly order_by?: string; -} -function encodeTxSearchParams(params: requests.TxSearchParams): RpcTxSearchParams { - return { - query: params.query, - prove: params.prove, - page: may(smallIntToApi, params.page), - per_page: may(smallIntToApi, params.per_page), - order_by: params.order_by, - }; -} - -interface RpcValidatorsParams { - readonly height?: string; - readonly page?: string; - readonly per_page?: string; -} -function encodeValidatorsParams(params: requests.ValidatorsParams): RpcValidatorsParams { - return { - height: may(smallIntToApi, params.height), - page: may(smallIntToApi, params.page), - per_page: may(smallIntToApi, params.per_page), - }; -} - -export class Params { - public static encodeAbciInfo(req: requests.AbciInfoRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method); - } - - public static encodeAbciQuery(req: requests.AbciQueryRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeAbciQueryParams(req.params)); - } - - public static encodeBlock(req: requests.BlockRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeHeightParam(req.params)); - } - - public static encodeBlockchain(req: requests.BlockchainRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeBlockchainRequestParams(req.params)); - } - - public static encodeBlockResults(req: requests.BlockResultsRequest): JsonRpcRequest { - 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)); - } - - public static encodeCommit(req: requests.CommitRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeHeightParam(req.params)); - } - - public static encodeGenesis(req: requests.GenesisRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method); - } - - public static encodeHealth(req: requests.HealthRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method); - } - - public static encodeNumUnconfirmedTxs(req: requests.NumUnconfirmedTxsRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method); - } - - public static encodeStatus(req: requests.StatusRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method); - } - - public static encodeSubscribe(req: requests.SubscribeRequest): JsonRpcRequest { - const eventTag = { key: "tm.event", value: req.query.type }; - const query = requests.buildQuery({ tags: [eventTag], raw: req.query.raw }); - return createJsonRpcRequest("subscribe", { query: query }); - } - - public static encodeTx(req: requests.TxRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeTxParams(req.params)); - } - - // TODO: encode params for query string??? - public static encodeTxSearch(req: requests.TxSearchRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeTxSearchParams(req.params)); - } - - public static encodeValidators(req: requests.ValidatorsRequest): JsonRpcRequest { - return createJsonRpcRequest(req.method, encodeValidatorsParams(req.params)); - } -} diff --git a/packages/tendermint-rpc/src/tendermint35/adaptor/responses.spec.ts b/packages/tendermint-rpc/src/tendermint35/adaptor/responses.spec.ts deleted file mode 100644 index c85b753328..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/adaptor/responses.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { fromBase64, fromHex } from "@cosmjs/encoding"; - -import { decodeEvent, decodeValidatorGenesis, decodeValidatorInfo, decodeValidatorUpdate } from "./responses"; - -describe("Adaptor Responses", () => { - describe("decodeEvent", () => { - it("works with attributes", () => { - // from https://rpc.mainnet-1.tgrade.confio.run/tx?hash=0x2C44715748022DB2FB5F40105383719BFCFCEE51DBC02FF4088BE3F5924CD7BF - const event = decodeEvent({ - type: "coin_spent", - attributes: [ - { key: "foo", value: "123" }, - { key: "bar", value: "456" }, - ], - }); - expect(event.type).toEqual("coin_spent"); - expect(event.attributes).toEqual([ - { key: "foo", value: "123" }, - { key: "bar", value: "456" }, - ]); - }); - - it("works with no attribute", () => { - const event = decodeEvent({ - type: "cosmos.module.EmittedEvent", - }); - expect(event.type).toEqual("cosmos.module.EmittedEvent"); - expect(event.attributes).toEqual([]); - }); - }); - - describe("decodeValidatorGenesis", () => { - it("works for genesis format", () => { - // from https://raw.githubusercontent.com/cosmos/mainnet/master/genesis.json - const validator = decodeValidatorGenesis({ - address: "A03DC128D38DB0BC5F18AE1872F1CB2E1FD41157", - name: "真本聪&IOSG", - power: "169980", - pub_key: { - type: "tendermint/PubKeyEd25519", - value: "2BX6Zuj8RmdJAkD1BAg6KB0v04liyM7jBdwOGIb9F9Q=", - }, - }); - expect(validator).toEqual({ - address: fromHex("A03DC128D38DB0BC5F18AE1872F1CB2E1FD41157"), - votingPower: BigInt(169980), - pubkey: { - algorithm: "ed25519", - data: fromBase64("2BX6Zuj8RmdJAkD1BAg6KB0v04liyM7jBdwOGIb9F9Q="), - }, - }); - }); - }); - - describe("decodeValidatorUpdate", () => { - it("works for block results format", () => { - // from https://rpc.cosmos.network/block_results?height=10539773 - const update = decodeValidatorUpdate({ - pub_key: { - Sum: { - type: "tendermint.crypto.PublicKey_Ed25519", - value: { - ed25519: "0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI=", - }, - }, - }, - power: "11418237", - }); - expect(update).toEqual({ - pubkey: { - algorithm: "ed25519", - data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="), - }, - votingPower: BigInt(11418237), - }); - }); - - it("works for block results format without voting power", () => { - // from https://rpc.cosmos.network/block_results?height=10883046 - const update = decodeValidatorUpdate({ - pub_key: { - Sum: { - type: "tendermint.crypto.PublicKey_Ed25519", - value: { - ed25519: "HjSC7VkhKih6xMhudlqfaFE8ZZnP8RKJPv4iqR7RhcE=", - }, - }, - }, - }); - expect(update).toEqual({ - pubkey: { - algorithm: "ed25519", - data: fromBase64("HjSC7VkhKih6xMhudlqfaFE8ZZnP8RKJPv4iqR7RhcE="), - }, - votingPower: BigInt(0), - }); - }); - }); - - describe("decodeValidatorInfo", () => { - it("works for validators format", () => { - // from https://rpc.cosmos.network/validators?height=10601034 - const info = decodeValidatorInfo({ - address: "AC2D56057CD84765E6FBE318979093E8E44AA18F", - pub_key: { - type: "tendermint/PubKeyEd25519", - value: "0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI=", - }, - voting_power: "11228980", - proposer_priority: "62870960", - }); - expect(info).toEqual({ - address: fromHex("AC2D56057CD84765E6FBE318979093E8E44AA18F"), - pubkey: { - algorithm: "ed25519", - data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="), - }, - votingPower: BigInt(11228980), - proposerPriority: 62870960, - }); - }); - }); -}); diff --git a/packages/tendermint-rpc/src/tendermint35/adaptor/responses.ts b/packages/tendermint-rpc/src/tendermint35/adaptor/responses.ts deleted file mode 100644 index 05aecedf8e..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/adaptor/responses.ts +++ /dev/null @@ -1,915 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { fromBase64, fromHex } from "@cosmjs/encoding"; -import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc"; -import { assert } from "@cosmjs/utils"; - -import { DateWithNanoseconds, fromRfc3339WithNanoseconds } from "../../dates"; -import { apiToBigInt, apiToSmallInt } from "../../inthelpers"; -import { SubscriptionEvent } from "../../rpcclients"; -import { BlockIdFlag, CommitSignature, ValidatorPubkey } from "../../types"; -import { - assertArray, - assertBoolean, - assertNotEmpty, - assertNumber, - assertObject, - assertSet, - assertString, - dictionaryToStringMap, - may, -} from "../encodings"; -import { hashTx } from "../hasher"; -import * as responses from "../responses"; - -interface AbciInfoResult { - readonly response: RpcAbciInfoResponse; -} - -interface RpcAbciInfoResponse { - readonly data?: string; - readonly last_block_height?: string; - /** base64 encoded */ - readonly last_block_app_hash?: string; -} - -function decodeAbciInfo(data: RpcAbciInfoResponse): responses.AbciInfoResponse { - return { - data: data.data, - lastBlockHeight: may(apiToSmallInt, data.last_block_height), - lastBlockAppHash: may(fromBase64, data.last_block_app_hash), - }; -} - -interface AbciQueryResult { - readonly response: RpcAbciQueryResponse; -} - -export interface RpcProofOp { - readonly type: string; - /** base64 encoded */ - readonly key: string; - /** base64 encoded */ - readonly data: string; -} - -export interface RpcQueryProof { - readonly ops: readonly RpcProofOp[]; -} - -function decodeQueryProof(data: RpcQueryProof): responses.QueryProof { - return { - ops: data.ops.map((op) => ({ - type: op.type, - key: fromBase64(op.key), - data: fromBase64(op.data), - })), - }; -} - -interface RpcAbciQueryResponse { - /** - * Base64 encoded - * - * This can be null since this is a byte slice and due to - * https://github.com/tendermint/tendermint/blob/v0.35.7/abci/types/result.go#L53 - */ - readonly key?: string | null; - /** - * Base64 encoded - * - * This can be null since this is a byte slice and due to - * https://github.com/tendermint/tendermint/blob/v0.35.7/abci/types/result.go#L53 - */ - readonly value?: string | null; - readonly proofOps?: RpcQueryProof | null; - readonly height?: string; - readonly index?: string; - readonly code?: string; // only for errors - readonly codespace?: string; - readonly log?: string; - readonly info?: string; -} - -function decodeAbciQuery(data: RpcAbciQueryResponse): responses.AbciQueryResponse { - return { - key: fromBase64(assertString(data.key ?? "")), - value: fromBase64(assertString(data.value ?? "")), - proof: may(decodeQueryProof, data.proofOps), - height: may(apiToSmallInt, data.height), - code: may(apiToSmallInt, data.code), - codespace: assertString(data.codespace ?? ""), - index: may(apiToSmallInt, data.index), - log: data.log, - info: assertString(data.info ?? ""), - }; -} - -/** - * EventAttribute from Tendermint. In 0.35 the type of key and value was changed - * from bytes to string, such that no base64 encoding is used anymore. - */ -interface RpcEventAttribute { - readonly key: string; - readonly value?: string; -} - -function decodeEventAttribute(attribute: RpcEventAttribute): responses.EventAttribute { - return { - key: assertNotEmpty(attribute.key), - value: attribute.value ?? "", - }; -} - -function decodeAttributes(attributes: readonly RpcEventAttribute[]): responses.EventAttribute[] { - return assertArray(attributes).map(decodeEventAttribute); -} - -interface RpcEvent { - readonly type: string; - /** Can be omitted (see https://github.com/cosmos/cosmjs/pull/1198) */ - readonly attributes?: readonly RpcEventAttribute[]; -} - -export function decodeEvent(event: RpcEvent): responses.Event { - return { - type: event.type, - attributes: event.attributes ? decodeAttributes(event.attributes) : [], - }; -} - -function decodeEvents(events: readonly RpcEvent[]): readonly responses.Event[] { - return assertArray(events).map(decodeEvent); -} - -interface RpcTxData { - readonly codespace?: string; - readonly code?: number; - readonly log?: string; - /** base64 encoded */ - readonly data?: string; - readonly events?: readonly RpcEvent[]; - readonly gas_wanted?: string; - readonly gas_used?: string; -} - -function decodeTxData(data: RpcTxData): responses.TxData { - return { - code: apiToSmallInt(assertNumber(data.code ?? 0)), - codespace: data.codespace, - log: data.log, - data: may(fromBase64, data.data), - events: data.events ? decodeEvents(data.events) : [], - gasWanted: apiToSmallInt(data.gas_wanted ?? "0"), - gasUsed: apiToSmallInt(data.gas_used ?? "0"), - }; -} - -type RpcPubkey = - | { - readonly type: string; - /** base64 encoded */ - readonly value: string; - } - | { - // See: https://github.com/cosmos/cosmjs/issues/1142 - readonly Sum: { - readonly type: string; - readonly value: { - /** base64 encoded */ - [algorithm: string]: string; - }; - }; - }; - -function decodePubkey(data: RpcPubkey): ValidatorPubkey { - if ("Sum" in data) { - // we don't need to check type because we're checking algorithm - const [[algorithm, value]] = Object.entries(data.Sum.value); - assert(algorithm === "ed25519" || algorithm === "secp256k1", `unknown pubkey type: ${algorithm}`); - return { - algorithm, - data: fromBase64(assertNotEmpty(value)), - }; - } else { - switch (data.type) { - // go-amino special code - case "tendermint/PubKeyEd25519": - return { - algorithm: "ed25519", - data: fromBase64(assertNotEmpty(data.value)), - }; - case "tendermint/PubKeySecp256k1": - return { - algorithm: "secp256k1", - data: fromBase64(assertNotEmpty(data.value)), - }; - default: - throw new Error(`unknown pubkey type: ${data.type}`); - } - } -} - -interface RpcBlockParams { - readonly max_bytes: string; - readonly max_gas: string; -} - -/** - * Note: we do not parse block.time_iota_ms for now because of this CHANGELOG entry - * - * > Add time_iota_ms to block's consensus parameters (not exposed to the application) - * https://github.com/tendermint/tendermint/blob/master/CHANGELOG.md#v0310 - */ -function decodeBlockParams(data: RpcBlockParams): responses.BlockParams { - return { - maxBytes: apiToSmallInt(assertNotEmpty(data.max_bytes)), - maxGas: apiToSmallInt(assertNotEmpty(data.max_gas)), - }; -} - -interface RpcEvidenceParams { - readonly max_age_num_blocks: string; - readonly max_age_duration: string; -} - -function decodeEvidenceParams(data: RpcEvidenceParams): responses.EvidenceParams { - return { - maxAgeNumBlocks: apiToSmallInt(assertNotEmpty(data.max_age_num_blocks)), - maxAgeDuration: apiToSmallInt(assertNotEmpty(data.max_age_duration)), - }; -} - -/** - * Example data: - * { - * "block": { - * "max_bytes": "22020096", - * "max_gas": "-1", - * "time_iota_ms": "1000" - * }, - * "evidence": { - * "max_age_num_blocks": "100000", - * "max_age_duration": "172800000000000" - * }, - * "validator": { - * "pub_key_types": [ - * "ed25519" - * ] - * } - * } - */ -interface RpcConsensusParams { - readonly block: RpcBlockParams; - readonly evidence: RpcEvidenceParams; -} - -function decodeConsensusParams(data: RpcConsensusParams): responses.ConsensusParams { - return { - block: decodeBlockParams(assertObject(data.block)), - evidence: decodeEvidenceParams(assertObject(data.evidence)), - }; -} - -// for block results -interface RpcValidatorUpdate { - readonly pub_key: RpcPubkey; - // When omitted, this means zero (see https://github.com/cosmos/cosmjs/issues/1177#issuecomment-1160115080) - readonly power?: string; -} - -export function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.ValidatorUpdate { - return { - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: apiToBigInt(data.power ?? "0"), - }; -} - -interface RpcBlockResultsResponse { - readonly height: string; - readonly txs_results: readonly RpcTxData[] | null; - readonly begin_block_events: readonly RpcEvent[] | null; - readonly end_block_events: readonly RpcEvent[] | null; - readonly validator_updates: readonly RpcValidatorUpdate[] | null; - readonly consensus_param_updates: RpcConsensusParams | null; -} - -function decodeBlockResults(data: RpcBlockResultsResponse): responses.BlockResultsResponse { - return { - height: apiToSmallInt(assertNotEmpty(data.height)), - results: (data.txs_results || []).map(decodeTxData), - validatorUpdates: (data.validator_updates || []).map(decodeValidatorUpdate), - consensusUpdates: may(decodeConsensusParams, data.consensus_param_updates), - beginBlockEvents: decodeEvents(data.begin_block_events || []), - endBlockEvents: decodeEvents(data.end_block_events || []), - }; -} - -interface RpcBlockId { - /** hex encoded */ - readonly hash: string; - readonly parts: { - readonly total: number; - /** hex encoded */ - readonly hash: string; - }; -} - -function decodeBlockId(data: RpcBlockId): responses.BlockId { - return { - hash: fromHex(assertNotEmpty(data.hash)), - parts: { - total: assertNotEmpty(data.parts.total), - hash: fromHex(assertNotEmpty(data.parts.hash)), - }, - }; -} - -interface RpcBlockVersion { - readonly block: string; - readonly app?: string; -} - -function decodeBlockVersion(data: RpcBlockVersion): responses.Version { - return { - block: apiToSmallInt(data.block), - app: apiToSmallInt(data.app ?? 0), - }; -} - -interface RpcHeader { - readonly version: RpcBlockVersion; - readonly chain_id: string; - readonly height: string; - readonly time: string; - - readonly last_block_id: RpcBlockId; - - /** hex encoded */ - readonly last_commit_hash: string; - /** hex encoded */ - readonly data_hash: string; - - /** hex encoded */ - readonly validators_hash: string; - /** hex encoded */ - readonly next_validators_hash: string; - /** hex encoded */ - readonly consensus_hash: string; - /** hex encoded */ - readonly app_hash: string; - /** hex encoded */ - readonly last_results_hash: string; - - /** hex encoded */ - readonly evidence_hash: string; - /** hex encoded */ - readonly proposer_address: string; -} - -function decodeHeader(data: RpcHeader): responses.Header { - return { - version: decodeBlockVersion(data.version), - chainId: assertNotEmpty(data.chain_id), - height: apiToSmallInt(assertNotEmpty(data.height)), - time: fromRfc3339WithNanoseconds(assertNotEmpty(data.time)), - - // When there is no last block ID (i.e. this block's height is 1), we get an empty structure like this: - // { hash: '', parts: { total: 0, hash: '' } } - lastBlockId: data.last_block_id.hash ? decodeBlockId(data.last_block_id) : null, - - lastCommitHash: fromHex(assertSet(data.last_commit_hash)), - dataHash: fromHex(assertSet(data.data_hash)), - - validatorsHash: fromHex(assertSet(data.validators_hash)), - nextValidatorsHash: fromHex(assertSet(data.next_validators_hash)), - consensusHash: fromHex(assertSet(data.consensus_hash)), - appHash: fromHex(assertSet(data.app_hash)), - lastResultsHash: fromHex(assertSet(data.last_results_hash)), - - evidenceHash: fromHex(assertSet(data.evidence_hash)), - proposerAddress: fromHex(assertNotEmpty(data.proposer_address)), - }; -} - -interface RpcBlockMeta { - readonly block_id: RpcBlockId; - readonly block_size: string; - readonly header: RpcHeader; - readonly num_txs: string; -} - -function decodeBlockMeta(data: RpcBlockMeta): responses.BlockMeta { - return { - blockId: decodeBlockId(data.block_id), - blockSize: apiToSmallInt(assertNotEmpty(data.block_size)), - header: decodeHeader(data.header), - numTxs: apiToSmallInt(assertNotEmpty(data.num_txs)), - }; -} - -interface RpcBlockchainResponse { - readonly last_height: string; - readonly block_metas: readonly RpcBlockMeta[]; -} - -function decodeBlockchain(data: RpcBlockchainResponse): responses.BlockchainResponse { - return { - lastHeight: apiToSmallInt(assertNotEmpty(data.last_height)), - blockMetas: assertArray(data.block_metas).map(decodeBlockMeta), - }; -} - -interface RpcBroadcastTxSyncResponse extends RpcTxData { - /** hex encoded */ - readonly hash: string; -} - -function decodeBroadcastTxSync(data: RpcBroadcastTxSyncResponse): responses.BroadcastTxSyncResponse { - return { - ...decodeTxData(data), - hash: fromHex(assertNotEmpty(data.hash)), - }; -} - -interface RpcBroadcastTxCommitResponse { - readonly height: string; - /** hex encoded */ - readonly hash: string; - readonly check_tx: RpcTxData; - readonly deliver_tx?: RpcTxData; -} - -function decodeBroadcastTxCommit(data: RpcBroadcastTxCommitResponse): responses.BroadcastTxCommitResponse { - return { - height: apiToSmallInt(data.height), - hash: fromHex(assertNotEmpty(data.hash)), - checkTx: decodeTxData(assertObject(data.check_tx)), - deliverTx: may(decodeTxData, data.deliver_tx), - }; -} - -function decodeBlockIdFlag(blockIdFlag: number): BlockIdFlag { - assert(blockIdFlag in BlockIdFlag); - return blockIdFlag; -} - -type RpcSignature = { - readonly block_id_flag: number; - /** hex encoded */ - readonly validator_address: string; - readonly timestamp: string; - /** - * Base64 encoded signature. - * There are cases when this is not set, see https://github.com/cosmos/cosmjs/issues/704#issuecomment-797122415. - */ - readonly signature: string | null; -}; - -/** - * In some cases a timestamp is optional and set to the value 0 in Go. - * This can lead to strings like "0001-01-01T00:00:00Z" (see https://github.com/cosmos/cosmjs/issues/704#issuecomment-797122415). - * This decoder tries to clean up such encoding from the API and turn them - * into undefined values. - */ -function decodeOptionalTime(timestamp: string): DateWithNanoseconds | undefined { - const nonZeroTime = timestamp && !timestamp.startsWith("0001-01-01"); - return nonZeroTime ? fromRfc3339WithNanoseconds(timestamp) : undefined; -} - -function decodeCommitSignature(data: RpcSignature): CommitSignature { - return { - blockIdFlag: decodeBlockIdFlag(data.block_id_flag), - validatorAddress: data.validator_address ? fromHex(data.validator_address) : undefined, - timestamp: decodeOptionalTime(data.timestamp), - signature: data.signature ? fromBase64(data.signature) : undefined, - }; -} - -interface RpcCommit { - readonly block_id: RpcBlockId; - readonly height: string; - readonly round: string; - readonly signatures: readonly RpcSignature[]; -} - -function decodeCommit(data: RpcCommit): responses.Commit { - return { - blockId: decodeBlockId(assertObject(data.block_id)), - height: apiToSmallInt(assertNotEmpty(data.height)), - round: apiToSmallInt(data.round), - signatures: assertArray(data.signatures).map(decodeCommitSignature), - }; -} - -interface RpcCommitResponse { - readonly signed_header: { - readonly header: RpcHeader; - readonly commit: RpcCommit; - }; - readonly canonical: boolean; -} - -function decodeCommitResponse(data: RpcCommitResponse): responses.CommitResponse { - return { - canonical: assertBoolean(data.canonical), - header: decodeHeader(data.signed_header.header), - commit: decodeCommit(data.signed_header.commit), - }; -} - -interface RpcValidatorGenesis { - /** hex-encoded */ - readonly address: string; - readonly pub_key: RpcPubkey; - readonly power: string; - readonly name?: string; -} - -export function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Validator { - return { - address: fromHex(assertNotEmpty(data.address)), - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: apiToBigInt(assertNotEmpty(data.power)), - }; -} - -interface RpcGenesisResponse { - readonly genesis_time: string; - readonly chain_id: string; - readonly consensus_params: RpcConsensusParams; - // The validators key is used to specify a set of validators for testnets or PoA blockchains. - // PoS blockchains use the app_state.genutil.gentxs field to stake and bond a number of validators in the first block. - readonly validators?: readonly RpcValidatorGenesis[]; - /** hex encoded */ - readonly app_hash: string; - readonly app_state: Record | undefined; -} - -interface GenesisResult { - readonly genesis: RpcGenesisResponse; -} - -function decodeGenesis(data: RpcGenesisResponse): responses.GenesisResponse { - return { - genesisTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.genesis_time)), - chainId: assertNotEmpty(data.chain_id), - consensusParams: decodeConsensusParams(data.consensus_params), - validators: data.validators ? assertArray(data.validators).map(decodeValidatorGenesis) : [], - appHash: fromHex(assertSet(data.app_hash)), // empty string in kvstore app - appState: data.app_state, - }; -} - -// this is in status -interface RpcValidatorInfo { - /** hex encoded */ - readonly address: string; - readonly pub_key: RpcPubkey; - readonly voting_power: string; - readonly proposer_priority?: string; -} - -export function decodeValidatorInfo(data: RpcValidatorInfo): responses.Validator { - return { - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: apiToBigInt(assertNotEmpty(data.voting_power)), - address: fromHex(assertNotEmpty(data.address)), - proposerPriority: data.proposer_priority ? apiToSmallInt(data.proposer_priority) : undefined, - }; -} - -interface RpcNodeInfo { - /** hex encoded */ - readonly id: string; - /** IP and port */ - readonly listen_addr: string; - readonly network: string; - readonly version: string; - readonly channels: string; // ??? - readonly moniker: string; - readonly protocol_version: { - readonly p2p: string; - readonly block: string; - readonly app: string; - }; - /** - * Additional information. E.g. - * { - * "tx_index": "on", - * "rpc_address":"tcp://0.0.0.0:26657" - * } - */ - readonly other: Record; -} - -function decodeNodeInfo(data: RpcNodeInfo): responses.NodeInfo { - return { - id: fromHex(assertNotEmpty(data.id)), - listenAddr: assertNotEmpty(data.listen_addr), - network: assertNotEmpty(data.network), - version: assertString(data.version), // Can be empty (https://github.com/cosmos/cosmos-sdk/issues/7963) - channels: assertNotEmpty(data.channels), - moniker: assertNotEmpty(data.moniker), - other: dictionaryToStringMap(data.other), - protocolVersion: { - app: apiToSmallInt(assertNotEmpty(data.protocol_version.app)), - block: apiToSmallInt(assertNotEmpty(data.protocol_version.block)), - p2p: apiToSmallInt(assertNotEmpty(data.protocol_version.p2p)), - }, - }; -} - -interface RpcSyncInfo { - /** hex encoded */ - readonly latest_block_hash: string; - /** hex encoded */ - readonly latest_app_hash: string; - readonly latest_block_height: string; - readonly latest_block_time: string; - readonly catching_up: boolean; -} - -function decodeSyncInfo(data: RpcSyncInfo): responses.SyncInfo { - return { - latestBlockHash: fromHex(assertNotEmpty(data.latest_block_hash)), - latestAppHash: fromHex(assertNotEmpty(data.latest_app_hash)), - latestBlockTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.latest_block_time)), - latestBlockHeight: apiToSmallInt(assertNotEmpty(data.latest_block_height)), - catchingUp: assertBoolean(data.catching_up), - }; -} - -interface RpcStatusResponse { - readonly node_info: RpcNodeInfo; - readonly sync_info: RpcSyncInfo; - readonly validator_info: RpcValidatorInfo; -} - -function decodeStatus(data: RpcStatusResponse): responses.StatusResponse { - return { - nodeInfo: decodeNodeInfo(data.node_info), - syncInfo: decodeSyncInfo(data.sync_info), - validatorInfo: decodeValidatorInfo(data.validator_info), - }; -} - -/** - * Example data: - * { - * "root_hash": "10A1A17D5F818099B5CAB5B91733A3CC27C0DB6CE2D571AC27FB970C314308BB", - * "data": "ZVlERVhDV2lVNEUwPXhTUjc4Tmp2QkNVSg==", - * "proof": { - * "total": "1", - * "index": "0", - * "leaf_hash": "EKGhfV+BgJm1yrW5FzOjzCfA22zi1XGsJ/uXDDFDCLs=", - * "aunts": [] - * } - * } - */ -interface RpcTxProof { - /** base64 encoded */ - readonly data: string; - /** hex encoded */ - readonly root_hash: string; - readonly proof: { - readonly total: string; - readonly index: string; - /** base64 encoded */ - readonly leaf_hash: string; - /** base64 encoded */ - readonly aunts: readonly string[]; - }; -} - -function decodeTxProof(data: RpcTxProof): responses.TxProof { - return { - data: fromBase64(assertNotEmpty(data.data)), - rootHash: fromHex(assertNotEmpty(data.root_hash)), - proof: { - total: apiToSmallInt(assertNotEmpty(data.proof.total)), - index: apiToSmallInt(assertNotEmpty(data.proof.index)), - leafHash: fromBase64(assertNotEmpty(data.proof.leaf_hash)), - aunts: assertArray(data.proof.aunts).map(fromBase64), - }, - }; -} - -interface RpcTxResponse { - /** Raw tx bytes, base64 encoded */ - readonly tx: string; - readonly tx_result: RpcTxData; - readonly height: string; - readonly index: number; - /** hex encoded */ - readonly hash: string; - readonly proof?: RpcTxProof; -} - -function decodeTxResponse(data: RpcTxResponse): responses.TxResponse { - return { - tx: fromBase64(assertNotEmpty(data.tx)), - result: decodeTxData(assertObject(data.tx_result)), - height: apiToSmallInt(assertNotEmpty(data.height)), - index: apiToSmallInt(assertNumber(data.index)), - hash: fromHex(assertNotEmpty(data.hash)), - proof: may(decodeTxProof, data.proof), - }; -} - -interface RpcTxSearchResponse { - readonly txs: readonly RpcTxResponse[]; - readonly total_count: string; -} - -function decodeTxSearch(data: RpcTxSearchResponse): responses.TxSearchResponse { - return { - totalCount: apiToSmallInt(assertNotEmpty(data.total_count)), - txs: assertArray(data.txs).map(decodeTxResponse), - }; -} - -interface RpcTxEvent { - /** Raw tx bytes, base64 encoded */ - readonly tx: string; - readonly result: RpcTxData; - readonly height: string; -} - -function decodeTxEvent(data: RpcTxEvent): responses.TxEvent { - const tx = fromBase64(assertNotEmpty(data.tx)); - return { - tx: tx, - hash: hashTx(tx), - result: decodeTxData(data.result), - height: apiToSmallInt(assertNotEmpty(data.height)), - }; -} - -interface RpcValidatorsResponse { - readonly block_height: string; - readonly validators: readonly RpcValidatorInfo[]; - readonly count: string; - readonly total: string; -} - -function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse { - return { - blockHeight: apiToSmallInt(assertNotEmpty(data.block_height)), - validators: assertArray(data.validators).map(decodeValidatorInfo), - count: apiToSmallInt(assertNotEmpty(data.count)), - total: apiToSmallInt(assertNotEmpty(data.total)), - }; -} - -// We lost track on how the evidence structure actually looks like. -// This is any now and passed to the caller untouched. -type RpcEvidence = any; - -interface RpcBlock { - readonly header: RpcHeader; - readonly last_commit: RpcCommit; - readonly data: { - /** Raw tx bytes, base64 encoded */ - readonly txs?: readonly string[]; - }; - // It's currently unclear why the deep nesting is requied. - // See https://github.com/tendermint/tendermint/issues/7697. - readonly evidence?: { - readonly evidence?: readonly RpcEvidence[]; - }; -} - -function decodeBlock(data: RpcBlock): responses.Block { - return { - header: decodeHeader(assertObject(data.header)), - // For the block at height 1, last commit is not set. This is represented in an empty object like this: - // { height: '0', round: 0, block_id: { hash: '', parts: [Object] }, signatures: [] } - lastCommit: data.last_commit.block_id.hash ? decodeCommit(assertObject(data.last_commit)) : null, - txs: data.data.txs ? assertArray(data.data.txs).map(fromBase64) : [], - // Lift up .evidence.evidence to just .evidence - // See https://github.com/tendermint/tendermint/issues/7697 - evidence: data.evidence?.evidence ?? [], - }; -} - -interface RpcBlockResponse { - readonly block_id: RpcBlockId; - readonly block: RpcBlock; -} - -function decodeBlockResponse(data: RpcBlockResponse): responses.BlockResponse { - return { - blockId: decodeBlockId(data.block_id), - block: decodeBlock(data.block), - }; -} - -interface RpcBlockSearchResponse { - readonly blocks: readonly RpcBlockResponse[]; - readonly total_count: string; -} - -function decodeBlockSearch(data: RpcBlockSearchResponse): responses.BlockSearchResponse { - return { - totalCount: apiToSmallInt(assertNotEmpty(data.total_count)), - blocks: assertArray(data.blocks).map(decodeBlockResponse), - }; -} - -interface RpcNumUnconfirmedTxsResponse { - readonly total: string; - readonly total_bytes: string; -} - -function decodeNumUnconfirmedTxs(data: RpcNumUnconfirmedTxsResponse): responses.NumUnconfirmedTxsResponse { - return { - total: apiToSmallInt(assertNotEmpty(data.total)), - totalBytes: apiToSmallInt(assertNotEmpty(data.total_bytes)), - }; -} - -export class Responses { - public static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse { - return decodeAbciInfo(assertObject((response.result as AbciInfoResult).response)); - } - - public static decodeAbciQuery(response: JsonRpcSuccessResponse): responses.AbciQueryResponse { - return decodeAbciQuery(assertObject((response.result as AbciQueryResult).response)); - } - - public static decodeBlock(response: JsonRpcSuccessResponse): responses.BlockResponse { - return decodeBlockResponse(response.result as RpcBlockResponse); - } - - public static decodeBlockResults(response: JsonRpcSuccessResponse): responses.BlockResultsResponse { - 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); - } - - public static decodeBroadcastTxSync(response: JsonRpcSuccessResponse): responses.BroadcastTxSyncResponse { - return decodeBroadcastTxSync(response.result as RpcBroadcastTxSyncResponse); - } - - public static decodeBroadcastTxAsync(response: JsonRpcSuccessResponse): responses.BroadcastTxAsyncResponse { - return Responses.decodeBroadcastTxSync(response); - } - - public static decodeBroadcastTxCommit( - response: JsonRpcSuccessResponse, - ): responses.BroadcastTxCommitResponse { - return decodeBroadcastTxCommit(response.result as RpcBroadcastTxCommitResponse); - } - - public static decodeCommit(response: JsonRpcSuccessResponse): responses.CommitResponse { - return decodeCommitResponse(response.result as RpcCommitResponse); - } - - public static decodeGenesis(response: JsonRpcSuccessResponse): responses.GenesisResponse { - return decodeGenesis(assertObject((response.result as GenesisResult).genesis)); - } - - public static decodeHealth(): responses.HealthResponse { - return null; - } - - public static decodeNumUnconfirmedTxs( - response: JsonRpcSuccessResponse, - ): responses.NumUnconfirmedTxsResponse { - return decodeNumUnconfirmedTxs(response.result as RpcNumUnconfirmedTxsResponse); - } - - public static decodeStatus(response: JsonRpcSuccessResponse): responses.StatusResponse { - return decodeStatus(response.result as RpcStatusResponse); - } - - public static decodeNewBlockEvent(event: SubscriptionEvent): responses.NewBlockEvent { - return decodeBlock(event.data.value.block as RpcBlock); - } - - public static decodeNewBlockHeaderEvent(event: SubscriptionEvent): responses.NewBlockHeaderEvent { - return decodeHeader(event.data.value.header as RpcHeader); - } - - public static decodeTxEvent(event: SubscriptionEvent): responses.TxEvent { - return decodeTxEvent(event.data.value.TxResult as RpcTxEvent); - } - - public static decodeTx(response: JsonRpcSuccessResponse): responses.TxResponse { - return decodeTxResponse(response.result as RpcTxResponse); - } - - public static decodeTxSearch(response: JsonRpcSuccessResponse): responses.TxSearchResponse { - return decodeTxSearch(response.result as RpcTxSearchResponse); - } - - public static decodeValidators(response: JsonRpcSuccessResponse): responses.ValidatorsResponse { - return decodeValidators(response.result as RpcValidatorsResponse); - } -} diff --git a/packages/tendermint-rpc/src/tendermint35/adaptor/types.ts b/packages/tendermint-rpc/src/tendermint35/adaptor/types.ts deleted file mode 100644 index 1558df6d8e..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/adaptor/types.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc"; - -import { SubscriptionEvent } from "../../rpcclients"; -import * as requests from "../requests"; -import * as responses from "../responses"; - -export interface Adaptor { - readonly params: Params; - readonly responses: Responses; - readonly hashTx: (tx: Uint8Array) => Uint8Array; - readonly hashBlock: (header: responses.Header) => Uint8Array; -} - -// Encoder is a generic that matches all methods of Params -export type Encoder = (req: T) => JsonRpcRequest; - -// Decoder is a generic that matches all methods of Responses -export type Decoder = (res: JsonRpcSuccessResponse) => T; - -export interface Params { - readonly encodeAbciInfo: (req: requests.AbciInfoRequest) => JsonRpcRequest; - readonly encodeAbciQuery: (req: requests.AbciQueryRequest) => JsonRpcRequest; - 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; - readonly encodeHealth: (req: requests.HealthRequest) => JsonRpcRequest; - readonly encodeNumUnconfirmedTxs: (req: requests.NumUnconfirmedTxsRequest) => JsonRpcRequest; - readonly encodeStatus: (req: requests.StatusRequest) => JsonRpcRequest; - readonly encodeSubscribe: (req: requests.SubscribeRequest) => JsonRpcRequest; - readonly encodeTx: (req: requests.TxRequest) => JsonRpcRequest; - readonly encodeTxSearch: (req: requests.TxSearchRequest) => JsonRpcRequest; - readonly encodeValidators: (req: requests.ValidatorsRequest) => JsonRpcRequest; -} - -export interface Responses { - readonly decodeAbciInfo: (response: JsonRpcSuccessResponse) => responses.AbciInfoResponse; - 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; - readonly decodeBroadcastTxCommit: (response: JsonRpcSuccessResponse) => responses.BroadcastTxCommitResponse; - readonly decodeCommit: (response: JsonRpcSuccessResponse) => responses.CommitResponse; - readonly decodeGenesis: (response: JsonRpcSuccessResponse) => responses.GenesisResponse; - readonly decodeHealth: (response: JsonRpcSuccessResponse) => responses.HealthResponse; - readonly decodeNumUnconfirmedTxs: (response: JsonRpcSuccessResponse) => responses.NumUnconfirmedTxsResponse; - readonly decodeStatus: (response: JsonRpcSuccessResponse) => responses.StatusResponse; - readonly decodeTx: (response: JsonRpcSuccessResponse) => responses.TxResponse; - readonly decodeTxSearch: (response: JsonRpcSuccessResponse) => responses.TxSearchResponse; - readonly decodeValidators: (response: JsonRpcSuccessResponse) => responses.ValidatorsResponse; - - // events - readonly decodeNewBlockEvent: (response: SubscriptionEvent) => responses.NewBlockEvent; - readonly decodeNewBlockHeaderEvent: (response: SubscriptionEvent) => responses.NewBlockHeaderEvent; - readonly decodeTxEvent: (response: SubscriptionEvent) => responses.TxEvent; -} diff --git a/packages/tendermint-rpc/src/tendermint35/encodings.spec.ts b/packages/tendermint-rpc/src/tendermint35/encodings.spec.ts deleted file mode 100644 index b4921d3b73..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/encodings.spec.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { ReadonlyDate } from "readonly-date"; - -import { - encodeBlockId, - encodeBytes, - encodeString, - encodeTime, - encodeUvarint, - encodeVersion, -} from "./encodings"; - -describe("encodings", () => { - describe("encodeString", () => { - it("works", () => { - expect(encodeString("")).toEqual(Uint8Array.from([0])); - const str = "hello iov"; - expect(encodeString(str)).toEqual( - Uint8Array.from([str.length, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6f, 0x76]), - ); - }); - }); - - describe("encodeUvarint", () => { - it("works", () => { - expect(encodeUvarint(0)).toEqual(Uint8Array.from([0])); - expect(encodeUvarint(1)).toEqual(Uint8Array.from([1])); - expect(encodeUvarint(127)).toEqual(Uint8Array.from([127])); - expect(encodeUvarint(128)).toEqual(Uint8Array.from([128, 1])); - expect(encodeUvarint(255)).toEqual(Uint8Array.from([255, 1])); - expect(encodeUvarint(256)).toEqual(Uint8Array.from([128, 2])); - }); - }); - - describe("encodeTime", () => { - it("works", () => { - const readonlyDateWithNanoseconds = new ReadonlyDate(1464109200); - (readonlyDateWithNanoseconds as any).nanoseconds = 666666; - expect(encodeTime(readonlyDateWithNanoseconds)).toEqual( - Uint8Array.from([0x08, 173, 174, 89, 0x10, 170, 220, 215, 95]), - ); - }); - }); - - describe("encodeBytes", () => { - it("works", () => { - expect(encodeBytes(Uint8Array.from([]))).toEqual(Uint8Array.from([])); - const uint8Array = Uint8Array.from([1, 2, 3, 4, 5, 6, 7]); - expect(encodeBytes(uint8Array)).toEqual(Uint8Array.from([uint8Array.length, 1, 2, 3, 4, 5, 6, 7])); - }); - }); - - describe("encodeVersion", () => { - it("works", () => { - const version = { - block: 666666, - app: 200, - }; - expect(encodeVersion(version)).toEqual(Uint8Array.from([0x08, 170, 216, 40, 0x10, 200, 1])); - }); - }); - - describe("encodeBlockId", () => { - it("works", () => { - const blockId = { - hash: Uint8Array.from([1, 2, 3, 4, 5, 6, 7]), - parts: { - total: 88, - hash: Uint8Array.from([8, 9, 10, 11, 12]), - }, - }; - expect(encodeBlockId(blockId)).toEqual( - Uint8Array.from([ - 0x0a, - blockId.hash.length, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 0x12, - 9, - 0x08, - 88, - 0x12, - 5, - 8, - 9, - 10, - 11, - 12, - ]), - ); - }); - }); -}); diff --git a/packages/tendermint-rpc/src/tendermint35/encodings.ts b/packages/tendermint-rpc/src/tendermint35/encodings.ts deleted file mode 100644 index 037b506898..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/encodings.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { toUtf8 } from "@cosmjs/encoding"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { BlockId, Version } from "./responses"; - -/** - * A runtime checker that ensures a given value is set (i.e. not undefined or null) - * - * This is used when you want to verify that data at runtime matches the expected type. - */ -export function assertSet(value: T): T { - if ((value as unknown) === undefined) { - throw new Error("Value must not be undefined"); - } - - if ((value as unknown) === null) { - throw new Error("Value must not be null"); - } - - return value; -} - -/** - * A runtime checker that ensures a given value is a boolean - * - * This is used when you want to verify that data at runtime matches the expected type. - * This implies assertSet. - */ -export function assertBoolean(value: boolean): boolean { - assertSet(value); - if (typeof (value as unknown) !== "boolean") { - throw new Error("Value must be a boolean"); - } - return value; -} - -/** - * A runtime checker that ensures a given value is a string. - * - * This is used when you want to verify that data at runtime matches the expected type. - * This implies assertSet. - */ -export function assertString(value: string): string { - assertSet(value); - if (typeof (value as unknown) !== "string") { - throw new Error("Value must be a string"); - } - return value; -} - -/** - * A runtime checker that ensures a given value is a number - * - * This is used when you want to verify that data at runtime matches the expected type. - * This implies assertSet. - */ -export function assertNumber(value: number): number { - assertSet(value); - if (typeof (value as unknown) !== "number") { - throw new Error("Value must be a number"); - } - return value; -} - -/** - * A runtime checker that ensures a given value is an array - * - * This is used when you want to verify that data at runtime matches the expected type. - * This implies assertSet. - */ -export function assertArray(value: readonly T[]): readonly T[] { - assertSet(value); - if (!Array.isArray(value as unknown)) { - throw new Error("Value must be a an array"); - } - return value; -} - -/** - * A runtime checker that ensures a given value is an object in the sense of JSON - * (an unordered collection of key–value pairs where the keys are strings) - * - * This is used when you want to verify that data at runtime matches the expected type. - * This implies assertSet. - */ -export function assertObject(value: T): T { - assertSet(value); - if (typeof (value as unknown) !== "object") { - throw new Error("Value must be an object"); - } - - // Exclude special kind of objects like Array, Date or Uint8Array - // Object.prototype.toString() returns a specified value: - // http://www.ecma-international.org/ecma-262/7.0/index.html#sec-object.prototype.tostring - if (Object.prototype.toString.call(value) !== "[object Object]") { - throw new Error("Value must be a simple object"); - } - - return value; -} - -interface Lengther { - readonly length: number; -} - -/** - * Throws an error if value matches the empty value for the - * given type (array/string of length 0, number of value 0, ...) - * - * Otherwise returns the value. - * - * This implies assertSet - */ -export function assertNotEmpty(value: T): T { - assertSet(value); - - if (typeof value === "number" && value === 0) { - throw new Error("must provide a non-zero value"); - } else if ((value as any as Lengther).length === 0) { - throw new Error("must provide a non-empty value"); - } - return value; -} - -// may will run the transform if value is defined, otherwise returns undefined -export function may(transform: (val: T) => U, value: T | null | undefined): U | undefined { - return value === undefined || value === null ? undefined : transform(value); -} - -export function dictionaryToStringMap(obj: Record): Map { - const out = new Map(); - for (const key of Object.keys(obj)) { - const value = obj[key]; - if (typeof value !== "string") { - throw new Error("Found dictionary value of type other than string"); - } - out.set(key, value); - } - return out; -} - -// Encodings needed for hashing block headers -// Several of these functions are inspired by https://github.com/nomic-io/js-tendermint/blob/tendermint-0.30/src/ - -// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L193-L195 -export function encodeString(s: string): Uint8Array { - const utf8 = toUtf8(s); - return Uint8Array.from([utf8.length, ...utf8]); -} - -// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L79-L87 -export function encodeUvarint(n: number): Uint8Array { - return n >= 0x80 - ? // eslint-disable-next-line no-bitwise - Uint8Array.from([(n & 0xff) | 0x80, ...encodeUvarint(n >> 7)]) - : // eslint-disable-next-line no-bitwise - Uint8Array.from([n & 0xff]); -} - -// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L134-L178 -export function encodeTime(time: ReadonlyDateWithNanoseconds): Uint8Array { - const milliseconds = time.getTime(); - const seconds = Math.floor(milliseconds / 1000); - const secondsArray = seconds ? [0x08, ...encodeUvarint(seconds)] : new Uint8Array(); - const nanoseconds = (time.nanoseconds || 0) + (milliseconds % 1000) * 1e6; - const nanosecondsArray = nanoseconds ? [0x10, ...encodeUvarint(nanoseconds)] : new Uint8Array(); - return Uint8Array.from([...secondsArray, ...nanosecondsArray]); -} - -// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L180-L187 -export function encodeBytes(bytes: Uint8Array): Uint8Array { - // Since we're only dealing with short byte arrays we don't need a full VarBuffer implementation yet - if (bytes.length >= 0x80) throw new Error("Not implemented for byte arrays of length 128 or more"); - return bytes.length ? Uint8Array.from([bytes.length, ...bytes]) : new Uint8Array(); -} - -export function encodeVersion(version: Version): Uint8Array { - const blockArray = version.block - ? Uint8Array.from([0x08, ...encodeUvarint(version.block)]) - : new Uint8Array(); - const appArray = version.app ? Uint8Array.from([0x10, ...encodeUvarint(version.app)]) : new Uint8Array(); - return Uint8Array.from([...blockArray, ...appArray]); -} - -export function encodeBlockId(blockId: BlockId): Uint8Array { - return Uint8Array.from([ - 0x0a, - blockId.hash.length, - ...blockId.hash, - 0x12, - blockId.parts.hash.length + 4, - 0x08, - blockId.parts.total, - 0x12, - blockId.parts.hash.length, - ...blockId.parts.hash, - ]); -} diff --git a/packages/tendermint-rpc/src/tendermint35/hasher.spec.ts b/packages/tendermint-rpc/src/tendermint35/hasher.spec.ts deleted file mode 100644 index c66448757c..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/hasher.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { fromBase64, fromHex } from "@cosmjs/encoding"; -import { ReadonlyDate } from "readonly-date"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { hashBlock, hashTx } from "./hasher"; - -describe("Hasher", () => { - it("creates transaction hash equal to local test", () => { - // This was taken from a result from /tx_search of some random test transaction - // curl "http://localhost:11127/tx_search?query=\"tx.hash='5CB2CF94A1097A4BC19258BC2353C3E76102B6D528458BE45C855DC5563C1DB2'\"" - const txId = fromHex("5CB2CF94A1097A4BC19258BC2353C3E76102B6D528458BE45C855DC5563C1DB2"); - const txData = fromBase64("YUpxZDY2NURaUDMxPWd2TzBPdnNrVWFWYg=="); - expect(hashTx(txData)).toEqual(txId); - }); - - it("creates block hash equal to local test for empty block", () => { - // This was taken from a result from /block of some random empty block - // curl "http://localhost:11133/block" - const blockId = fromHex("153C484DCBC33633F0616BC019388C93DEA94F7880627976F2BFE83749E062F7"); - const time = new ReadonlyDate("2020-06-23T13:54:15.4638668Z"); - (time as any).nanoseconds = 866800; - const blockData = { - version: { - block: 10, - app: 1, - }, - chainId: "test-chain-2A5rwi", - height: 7795, - time: time as ReadonlyDateWithNanoseconds, - - lastBlockId: { - hash: fromHex("1EC48444E64E7B96585BA518613612E52B976E3DA2F2222B9CD4D1602656C96F"), - parts: { - total: 1, - hash: fromHex("D4E6F1B0EE08D0438C9BB8455D7D3F2FC1883C32D66F7C69C4A0F093B073F6D2"), - }, - }, - - lastCommitHash: fromHex("BA6A5EEA6687ACA8EE4FFE4F5D40EA073CB7397A5336309C3EC824805AF9723E"), - dataHash: fromHex(""), - - validatorsHash: fromHex("0BEEBC6AB3B7D4FE21E22B609CD4AEC7E121A42C07604FF1827651F0173745EB"), - nextValidatorsHash: fromHex("0BEEBC6AB3B7D4FE21E22B609CD4AEC7E121A42C07604FF1827651F0173745EB"), - consensusHash: fromHex("048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F"), - appHash: fromHex("8801000000000000"), - lastResultsHash: fromHex(""), - - evidenceHash: fromHex(""), - proposerAddress: fromHex("614F305502F65C01114F9B8711D9A0AB0AC369F4"), - }; - expect(hashBlock(blockData)).toEqual(blockId); - }); - - it("creates block hash equal to local test for block with a transaction", () => { - // This was taken from a result from /block of some random block with a transaction - // curl "http://localhost:11133/block?height=13575" - const blockId = fromHex("FF2995AF1F38B9A584077E53B5E144778718FB86539A51886A2C55F730403373"); - const time = new ReadonlyDate("2020-06-23T15:34:12.3232688Z"); - (time as any).nanoseconds = 268800; - const blockData = { - version: { - block: 10, - app: 1, - }, - chainId: "test-chain-2A5rwi", - height: 13575, - time: time as ReadonlyDateWithNanoseconds, - - lastBlockId: { - hash: fromHex("046D5441FC4D008FCDBF9F3DD5DC25CF00883763E44CF4FAF3923FB5FEA42D8F"), - parts: { - total: 1, - hash: fromHex("02E4715343625093C717638EAC67FB3A4B24CCC8DA610E0CB324D705E68FEF7B"), - }, - }, - - lastCommitHash: fromHex("AA2B807F3B0ACC866AB58D90C2D0FC70B6C860CFAC440590B4F590CDC178A207"), - dataHash: fromHex("56782879F526889734BA65375CD92A9152C7114B2C91B2D2AD8464FF69E884AA"), - - validatorsHash: fromHex("0BEEBC6AB3B7D4FE21E22B609CD4AEC7E121A42C07604FF1827651F0173745EB"), - nextValidatorsHash: fromHex("0BEEBC6AB3B7D4FE21E22B609CD4AEC7E121A42C07604FF1827651F0173745EB"), - consensusHash: fromHex("048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F"), - appHash: fromHex("CC02000000000000"), - lastResultsHash: fromHex("6E340B9CFFB37A989CA544E6BB780A2C78901D3FB33738768511A30617AFA01D"), - - evidenceHash: fromHex(""), - proposerAddress: fromHex("614F305502F65C01114F9B8711D9A0AB0AC369F4"), - }; - expect(hashBlock(blockData)).toEqual(blockId); - }); -}); diff --git a/packages/tendermint-rpc/src/tendermint35/hasher.ts b/packages/tendermint-rpc/src/tendermint35/hasher.ts deleted file mode 100644 index a02d2f814f..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/hasher.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Sha256, sha256 } from "@cosmjs/crypto"; - -import { - encodeBlockId, - encodeBytes, - encodeString, - encodeTime, - encodeUvarint, - encodeVersion, -} from "./encodings"; -import { Header } from "./responses"; - -// hash is sha256 -// https://github.com/tendermint/tendermint/blob/master/UPGRADING.md#v0260 -export function hashTx(tx: Uint8Array): Uint8Array { - return sha256(tx); -} - -function getSplitPoint(n: number): number { - if (n < 1) throw new Error("Cannot split an empty tree"); - const largestPowerOf2 = 2 ** Math.floor(Math.log2(n)); - return largestPowerOf2 < n ? largestPowerOf2 : largestPowerOf2 / 2; -} - -function hashLeaf(leaf: Uint8Array): Uint8Array { - const hash = new Sha256(Uint8Array.from([0])); - hash.update(leaf); - return hash.digest(); -} - -function hashInner(left: Uint8Array, right: Uint8Array): Uint8Array { - const hash = new Sha256(Uint8Array.from([1])); - hash.update(left); - hash.update(right); - return hash.digest(); -} - -// See https://github.com/tendermint/tendermint/blob/v0.31.8/docs/spec/blockchain/encoding.md#merkleroot -// Note: the hashes input may not actually be hashes, especially before a recursive call -function hashTree(hashes: readonly Uint8Array[]): Uint8Array { - switch (hashes.length) { - case 0: - throw new Error("Cannot hash empty tree"); - case 1: - return hashLeaf(hashes[0]); - default: { - const slicePoint = getSplitPoint(hashes.length); - const left = hashTree(hashes.slice(0, slicePoint)); - const right = hashTree(hashes.slice(slicePoint)); - return hashInner(left, right); - } - } -} - -export function hashBlock(header: Header): Uint8Array { - if (!header.lastBlockId) { - throw new Error( - "Hashing a block header with no last block ID (i.e. header at height 1) is not supported. If you need this, contributions are welcome. Please add documentation and test vectors for this case.", - ); - } - - const encodedFields: readonly Uint8Array[] = [ - encodeVersion(header.version), - encodeString(header.chainId), - encodeUvarint(header.height), - encodeTime(header.time), - encodeBlockId(header.lastBlockId), - - encodeBytes(header.lastCommitHash), - encodeBytes(header.dataHash), - encodeBytes(header.validatorsHash), - encodeBytes(header.nextValidatorsHash), - encodeBytes(header.consensusHash), - encodeBytes(header.appHash), - encodeBytes(header.lastResultsHash), - encodeBytes(header.evidenceHash), - encodeBytes(header.proposerAddress), - ]; - return hashTree(encodedFields); -} diff --git a/packages/tendermint-rpc/src/tendermint35/index.ts b/packages/tendermint-rpc/src/tendermint35/index.ts deleted file mode 100644 index 468b2e98ba..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Note: all exports in this module are publicly available via -// `import { tendermint35 } from "@cosmjs/tendermint-rpc"` - -export { - AbciInfoRequest, - AbciQueryParams, - AbciQueryRequest, - BlockchainRequest, - BlockRequest, - BlockResultsRequest, - BlockSearchParams, - BlockSearchRequest, - BroadcastTxParams, - BroadcastTxRequest, - CommitRequest, - GenesisRequest, - HealthRequest, - Method, - NumUnconfirmedTxsRequest, - QueryTag, - Request, - StatusRequest, - SubscriptionEventType, - TxParams, - TxRequest, - TxSearchParams, - TxSearchRequest, - ValidatorsParams, - ValidatorsRequest, -} from "./requests"; -export { - AbciInfoResponse, - AbciQueryResponse, - EventAttribute as Attribute, - Block, - BlockchainResponse, - BlockGossipParams, - BlockId, - BlockMeta, - BlockParams, - BlockResponse, - BlockResultsResponse, - BlockSearchResponse, - BroadcastTxAsyncResponse, - BroadcastTxCommitResponse, - broadcastTxCommitSuccess, - BroadcastTxSyncResponse, - broadcastTxSyncSuccess, - Commit, - CommitResponse, - ConsensusParams, - Event, - Evidence, - EvidenceParams, - GenesisResponse, - Header, - HealthResponse, - NewBlockEvent, - NewBlockHeaderEvent, - NodeInfo, - NumUnconfirmedTxsResponse, - ProofOp, - QueryProof, - Response, - StatusResponse, - SyncInfo, - TxData, - TxEvent, - TxProof, - TxResponse, - TxSearchResponse, - TxSizeParams, - Validator, - ValidatorsResponse, - Version, - Vote, - VoteType, -} from "./responses"; -export { Tendermint35Client } from "./tendermint35client"; diff --git a/packages/tendermint-rpc/src/tendermint35/requests.spec.ts b/packages/tendermint-rpc/src/tendermint35/requests.spec.ts deleted file mode 100644 index f2134ea8d6..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/requests.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { buildQuery } from "./requests"; - -describe("Requests", () => { - describe("buildQuery", () => { - it("works for no input", () => { - const query = buildQuery({}); - expect(query).toEqual(""); - }); - - it("works for one tags", () => { - const query = buildQuery({ tags: [{ key: "abc", value: "def" }] }); - expect(query).toEqual("abc='def'"); - }); - - it("works for two tags", () => { - const query = buildQuery({ - tags: [ - { key: "k", value: "9" }, - { key: "L", value: "7" }, - ], - }); - expect(query).toEqual("k='9' AND L='7'"); - }); - - it("works for raw input", () => { - const query = buildQuery({ raw: "aabbCCDD" }); - expect(query).toEqual("aabbCCDD"); - }); - - it("works for mixed input", () => { - const query = buildQuery({ - tags: [ - { key: "k", value: "9" }, - { key: "L", value: "7" }, - ], - raw: "aabbCCDD", - }); - expect(query).toEqual("k='9' AND L='7' AND aabbCCDD"); - }); - }); -}); diff --git a/packages/tendermint-rpc/src/tendermint35/requests.ts b/packages/tendermint-rpc/src/tendermint35/requests.ts deleted file mode 100644 index e4f131c5c0..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/requests.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -/** - * RPC methods as documented in https://docs.tendermint.com/master/rpc/ - * - * Enum raw value must match the spelling in the "shell" example call (snake_case) - */ -export enum Method { - AbciInfo = "abci_info", - AbciQuery = "abci_query", - Block = "block", - /** 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", - Commit = "commit", - Genesis = "genesis", - Health = "health", - NumUnconfirmedTxs = "num_unconfirmed_txs", - Status = "status", - Subscribe = "subscribe", - Tx = "tx", - TxSearch = "tx_search", - Validators = "validators", - Unsubscribe = "unsubscribe", -} - -export type Request = - | AbciInfoRequest - | AbciQueryRequest - | BlockRequest - | BlockSearchRequest - | BlockchainRequest - | BlockResultsRequest - | BroadcastTxRequest - | CommitRequest - | GenesisRequest - | HealthRequest - | NumUnconfirmedTxsRequest - | StatusRequest - | TxRequest - | TxSearchRequest - | ValidatorsRequest; - -/** - * Raw values must match the tendermint event name - * - * @see https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants - */ -export enum SubscriptionEventType { - NewBlock = "NewBlock", - NewBlockHeader = "NewBlockHeader", - Tx = "Tx", -} - -export interface AbciInfoRequest { - readonly method: Method.AbciInfo; -} - -export interface AbciQueryRequest { - readonly method: Method.AbciQuery; - readonly params: AbciQueryParams; -} - -export interface AbciQueryParams { - readonly path: string; - readonly data: Uint8Array; - readonly height?: number; - /** - * A flag that defines if proofs are included in the response or not. - * - * Internally this is mapped to the old inverse name `trusted` for Tendermint < 0.26. - * Starting with Tendermint 0.26, the default value changed from true to false. - */ - readonly prove?: boolean; -} - -export interface BlockRequest { - readonly method: Method.Block; - readonly params: { - readonly height?: number; - }; -} - -export interface BlockchainRequest { - readonly method: Method.Blockchain; - readonly params: BlockchainRequestParams; -} - -export interface BlockchainRequestParams { - readonly minHeight?: number; - readonly maxHeight?: number; -} - -export interface BlockResultsRequest { - readonly method: Method.BlockResults; - readonly params: { - readonly height?: number; - }; -} - -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; -} - -export interface BroadcastTxParams { - readonly tx: Uint8Array; -} - -export interface CommitRequest { - readonly method: Method.Commit; - readonly params: { - readonly height?: number; - }; -} - -export interface GenesisRequest { - readonly method: Method.Genesis; -} - -export interface HealthRequest { - readonly method: Method.Health; -} - -export interface NumUnconfirmedTxsRequest { - readonly method: Method.NumUnconfirmedTxs; -} - -export interface StatusRequest { - readonly method: Method.Status; -} - -export interface SubscribeRequest { - readonly method: Method.Subscribe; - readonly query: { - readonly type: SubscriptionEventType; - readonly raw?: string; - }; -} - -export interface QueryTag { - readonly key: string; - readonly value: string; -} - -export interface TxRequest { - readonly method: Method.Tx; - readonly params: TxParams; -} - -export interface TxParams { - readonly hash: Uint8Array; - readonly prove?: boolean; -} - -// TODO: clarify this type -export interface TxSearchRequest { - readonly method: Method.TxSearch; - readonly params: TxSearchParams; -} - -export interface TxSearchParams { - readonly query: string; - readonly prove?: boolean; - readonly page?: number; - readonly per_page?: number; - readonly order_by?: string; -} - -export interface ValidatorsRequest { - readonly method: Method.Validators; - readonly params: ValidatorsParams; -} - -export interface ValidatorsParams { - readonly height?: number; - readonly page?: number; - readonly per_page?: number; -} - -export interface BuildQueryComponents { - readonly tags?: readonly QueryTag[]; - readonly raw?: string; -} - -export function buildQuery(components: BuildQueryComponents): string { - const tags = components.tags ? components.tags : []; - const tagComponents = tags.map((tag) => `${tag.key}='${tag.value}'`); - const rawComponents = components.raw ? [components.raw] : []; - - return [...tagComponents, ...rawComponents].join(" AND "); -} diff --git a/packages/tendermint-rpc/src/tendermint35/responses.ts b/packages/tendermint-rpc/src/tendermint35/responses.ts deleted file mode 100644 index 66eb464c77..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/responses.ts +++ /dev/null @@ -1,389 +0,0 @@ -import { ReadonlyDate } from "readonly-date"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { CommitSignature, ValidatorPubkey } from "../types"; - -export type Response = - | AbciInfoResponse - | AbciQueryResponse - | BlockResponse - | BlockResultsResponse - | BlockSearchResponse - | BlockchainResponse - | BroadcastTxAsyncResponse - | BroadcastTxSyncResponse - | BroadcastTxCommitResponse - | CommitResponse - | GenesisResponse - | HealthResponse - | NumUnconfirmedTxsResponse - | StatusResponse - | TxResponse - | TxSearchResponse - | ValidatorsResponse; - -export interface AbciInfoResponse { - readonly data?: string; - readonly lastBlockHeight?: number; - readonly lastBlockAppHash?: Uint8Array; -} - -export interface ProofOp { - readonly type: string; - readonly key: Uint8Array; - readonly data: Uint8Array; -} - -export interface QueryProof { - readonly ops: readonly ProofOp[]; -} - -export interface AbciQueryResponse { - readonly key: Uint8Array; - readonly value: Uint8Array; - readonly proof?: QueryProof; - readonly height?: number; - readonly index?: number; - readonly code?: number; // non-falsy for errors - readonly codespace: string; - readonly log?: string; - readonly info: string; -} - -export interface BlockResponse { - readonly blockId: BlockId; - readonly block: Block; -} - -export interface BlockResultsResponse { - readonly height: number; - readonly results: readonly TxData[]; - readonly validatorUpdates: readonly ValidatorUpdate[]; - readonly consensusUpdates?: ConsensusParams; - readonly beginBlockEvents: readonly Event[]; - readonly endBlockEvents: readonly Event[]; -} - -export interface BlockSearchResponse { - readonly blocks: readonly BlockResponse[]; - readonly totalCount: number; -} - -export interface BlockchainResponse { - readonly lastHeight: number; - readonly blockMetas: readonly BlockMeta[]; -} - -/** - * No transaction data in here because RPC method BroadcastTxAsync "returns right away, with no response" - */ -export interface BroadcastTxAsyncResponse { - readonly hash: Uint8Array; -} - -export interface BroadcastTxSyncResponse extends TxData { - readonly hash: Uint8Array; -} - -/** - * Returns true iff transaction made it successfully into the transaction pool - */ -export function broadcastTxSyncSuccess(res: BroadcastTxSyncResponse): boolean { - // code must be 0 on success - return res.code === 0; -} - -export interface BroadcastTxCommitResponse { - readonly height: number; - readonly hash: Uint8Array; - readonly checkTx: TxData; - readonly deliverTx?: TxData; -} - -/** - * Returns true iff transaction made it successfully into a block - * (i.e. success in `check_tx` and `deliver_tx` field) - */ -export function broadcastTxCommitSuccess(response: BroadcastTxCommitResponse): boolean { - // code must be 0 on success - // deliverTx may be present but empty on failure - return response.checkTx.code === 0 && !!response.deliverTx && response.deliverTx.code === 0; -} - -export interface CommitResponse { - readonly header: Header; - readonly commit: Commit; - readonly canonical: boolean; -} - -export interface GenesisResponse { - readonly genesisTime: ReadonlyDate; - readonly chainId: string; - readonly consensusParams: ConsensusParams; - readonly validators: readonly Validator[]; - readonly appHash: Uint8Array; - readonly appState: Record | undefined; -} - -export type HealthResponse = null; - -export interface NumUnconfirmedTxsResponse { - readonly total: number; - readonly totalBytes: number; -} - -export interface StatusResponse { - readonly nodeInfo: NodeInfo; - readonly syncInfo: SyncInfo; - readonly validatorInfo: Validator; -} - -/** - * A transaction from RPC calls like search. - * - * Try to keep this compatible to TxEvent - */ -export interface TxResponse { - readonly tx: Uint8Array; - readonly hash: Uint8Array; - readonly height: number; - readonly index: number; - readonly result: TxData; - readonly proof?: TxProof; -} - -export interface TxSearchResponse { - readonly txs: readonly TxResponse[]; - readonly totalCount: number; -} - -export interface ValidatorsResponse { - readonly blockHeight: number; - readonly validators: readonly Validator[]; - readonly count: number; - readonly total: number; -} - -// Events - -export interface NewBlockEvent extends Block {} - -export interface NewBlockHeaderEvent extends Header {} - -export interface TxEvent { - readonly tx: Uint8Array; - readonly hash: Uint8Array; - readonly height: number; - readonly result: TxData; -} - -// Helper items used above - -/** - * An event attribute. - * - * In 0.35 the type of key and value was changed - * from bytes to string, such that no base64 encoding is used anymore. - */ -export interface EventAttribute { - readonly key: string; - readonly value: string; -} - -export interface Event { - readonly type: string; - readonly attributes: readonly EventAttribute[]; -} - -export interface TxData { - readonly code: number; - readonly codespace?: string; - readonly log?: string; - readonly data?: Uint8Array; - readonly events: readonly Event[]; - readonly gasWanted: number; - readonly gasUsed: number; -} - -export interface TxProof { - readonly data: Uint8Array; - readonly rootHash: Uint8Array; - readonly proof: { - readonly total: number; - readonly index: number; - readonly leafHash: Uint8Array; - readonly aunts: readonly Uint8Array[]; - }; -} - -export interface BlockMeta { - readonly blockId: BlockId; - readonly blockSize: number; - readonly header: Header; - readonly numTxs: number; -} - -export interface BlockId { - readonly hash: Uint8Array; - readonly parts: { - readonly total: number; - readonly hash: Uint8Array; - }; -} - -export interface Block { - readonly header: Header; - /** - * For the block at height 1, last commit is not set. - */ - readonly lastCommit: Commit | null; - readonly txs: readonly Uint8Array[]; - readonly evidence: readonly Evidence[]; -} - -/** - * We lost track on how the evidence structure actually looks like. - * This is any now and passed to the caller untouched. - * - * See also https://github.com/cosmos/cosmjs/issues/980. - */ -export type Evidence = any; - -export interface Commit { - readonly blockId: BlockId; - readonly height: number; - readonly round: number; - readonly signatures: readonly CommitSignature[]; -} - -/** - * raw values from https://github.com/tendermint/tendermint/blob/dfa9a9a30a666132425b29454e90a472aa579a48/types/vote.go#L44 - */ -export enum VoteType { - PreVote = 1, - PreCommit = 2, -} - -export interface Vote { - readonly type: VoteType; - readonly validatorAddress: Uint8Array; - readonly validatorIndex: number; - readonly height: number; - readonly round: number; - readonly timestamp: ReadonlyDate; - readonly blockId: BlockId; - readonly signature: Uint8Array; -} - -export interface Version { - readonly block: number; - readonly app: number; -} - -// https://github.com/tendermint/tendermint/blob/v0.31.8/docs/spec/blockchain/blockchain.md -export interface Header { - // basic block info - readonly version: Version; - readonly chainId: string; - readonly height: number; - readonly time: ReadonlyDateWithNanoseconds; - - /** - * Block ID of the previous block. This can be `null` when the currect block is height 1. - */ - readonly lastBlockId: BlockId | null; - - /** - * Hashes of block data. - * - * This is `sha256("")` for height 1 🤷‍ - */ - readonly lastCommitHash: Uint8Array; - /** - * This is `sha256("")` as long as there is no data 🤷‍ - */ - readonly dataHash: Uint8Array; - - // hashes from the app output from the prev block - readonly validatorsHash: Uint8Array; - readonly nextValidatorsHash: Uint8Array; - readonly consensusHash: Uint8Array; - /** - * This can be an empty string for height 1 and turn into "0000000000000000" later on 🤷‍ - */ - readonly appHash: Uint8Array; - /** - * This is `sha256("")` as long as there is no data 🤷‍ - */ - readonly lastResultsHash: Uint8Array; - - // consensus info - /** - * This is `sha256("")` as long as there is no data 🤷‍ - */ - readonly evidenceHash: Uint8Array; - readonly proposerAddress: Uint8Array; -} - -export interface NodeInfo { - readonly id: Uint8Array; - /** IP and port */ - readonly listenAddr: string; - readonly network: string; - /** - * The Tendermint version. Can be empty (see https://github.com/cosmos/cosmos-sdk/issues/7963). - */ - readonly version: string; - readonly channels: string; // ??? - readonly moniker: string; - readonly other: Map; - readonly protocolVersion: { - readonly p2p: number; - readonly block: number; - readonly app: number; - }; -} - -export interface SyncInfo { - readonly latestBlockHash: Uint8Array; - readonly latestAppHash: Uint8Array; - readonly latestBlockHeight: number; - readonly latestBlockTime: ReadonlyDate; - readonly catchingUp: boolean; -} - -export interface Validator { - readonly address: Uint8Array; - readonly pubkey?: ValidatorPubkey; - readonly votingPower: bigint; - readonly proposerPriority?: number; -} - -export interface ValidatorUpdate { - readonly pubkey: ValidatorPubkey; - readonly votingPower: bigint; -} - -export interface ConsensusParams { - readonly block: BlockParams; - readonly evidence: EvidenceParams; -} - -export interface BlockParams { - readonly maxBytes: number; - readonly maxGas: number; -} - -export interface TxSizeParams { - readonly maxBytes: number; - readonly maxGas: number; -} - -export interface BlockGossipParams { - readonly blockPartSizeBytes: number; -} - -export interface EvidenceParams { - readonly maxAgeNumBlocks: number; - readonly maxAgeDuration: number; -} diff --git a/packages/tendermint-rpc/src/tendermint35/tendermint35client.spec.ts b/packages/tendermint-rpc/src/tendermint35/tendermint35client.spec.ts deleted file mode 100644 index d46b95cc88..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/tendermint35client.spec.ts +++ /dev/null @@ -1,866 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { toAscii } from "@cosmjs/encoding"; -import { firstEvent, toListPromise } from "@cosmjs/stream"; -import { sleep } from "@cosmjs/utils"; -import { ReadonlyDate } from "readonly-date"; -import { Stream } from "xstream"; - -import { HttpClient, RpcClient, WebsocketClient } from "../rpcclients"; -import { - buildKvTx, - ExpectedValues, - nonNegativeIntegerMatcher, - pendingWithoutTendermint, - randomString, - tendermintEnabled, - tendermintInstances, - tendermintSearchIndexUpdated, -} from "../testutil.spec"; -import { adaptor35 } from "./adaptor"; -import { buildQuery } from "./requests"; -import * as responses from "./responses"; -import { Tendermint35Client } from "./tendermint35client"; - -function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues): void { - describe("create", () => { - it("can auto-discover Tendermint version and communicate", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - }); - - it("can connect to Tendermint with known version", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - expect(await client.abciInfo()).toBeTruthy(); - client.disconnect(); - }); - }); - - it("can get genesis", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const genesis = await client.genesis(); - expect(genesis).toBeTruthy(); - client.disconnect(); - }); - - describe("broadcastTxCommit", () => { - it("can broadcast a transaction", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - - const response = await client.broadcastTxCommit({ tx: tx }); - expect(response.height).toBeGreaterThan(2); - expect(response.hash).toBeTruthy(); - // verify success - expect(response.checkTx.code).toBeFalsy(); - expect(response.deliverTx).toBeTruthy(); - if (response.deliverTx) { - expect(response.deliverTx.code).toBeFalsy(); - } - - client.disconnect(); - }); - }); - - describe("broadcastTxSync", () => { - it("can broadcast a transaction", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - - const response = await client.broadcastTxSync({ tx: tx }); - expect(response.hash.length).toEqual(32); - // verify success - expect(response.code).toBeFalsy(); - expect(response.codespace).toBeFalsy(); - - client.disconnect(); - }); - }); - - describe("broadcastTxAsync", () => { - it("can broadcast a transaction", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - - const response = await client.broadcastTxAsync({ tx: tx }); - expect(response.hash.length).toEqual(32); - - client.disconnect(); - }); - }); - - it("gets the same tx hash from backend as calculated locally", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - const calculatedTxHash = adaptor35.hashTx(tx); - - const response = await client.broadcastTxCommit({ tx: tx }); - expect(response.hash).toEqual(calculatedTxHash); - - client.disconnect(); - }); - - describe("abciQuery", () => { - it("can query the state", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const key = randomString(); - const value = randomString(); - await client.broadcastTxCommit({ tx: buildKvTx(key, value) }); - - const binKey = toAscii(key); - const binValue = toAscii(value); - const queryParams = { path: "/key", data: binKey, prove: true }; - const response = await client.abciQuery(queryParams); - expect(response.key).toEqual(binKey); - expect(response.value).toEqual(binValue); - expect(response.code).toEqual(0); - expect(response.codespace).toEqual(""); - expect(response.index).toEqual(-1); - expect(response.proof).toBeUndefined(); - expect(response.log).toEqual("exists"); - expect(response.info).toEqual(""); - expect(response.height).toMatch(nonNegativeIntegerMatcher); - - client.disconnect(); - }); - }); - - it("can get a commit", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const response = await client.commit(4); - - expect(response).toBeTruthy(); - expect(response.commit.signatures.length).toBeGreaterThanOrEqual(1); - expect(response.commit.signatures[0].blockIdFlag).toEqual(2); - expect(response.commit.signatures[0].validatorAddress?.length).toEqual(20); - expect(response.commit.signatures[0].timestamp).toBeInstanceOf(Date); - expect(response.commit.signatures[0].signature?.length).toEqual(64); - - client.disconnect(); - }); - - it("can get validators", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const response = await client.validators({}); - - expect(response).toBeTruthy(); - expect(response.blockHeight).toBeGreaterThanOrEqual(1); - expect(response.count).toBeGreaterThanOrEqual(1); - expect(response.total).toBeGreaterThanOrEqual(1); - expect(response.validators.length).toBeGreaterThanOrEqual(1); - expect(response.validators[0].address.length).toEqual(20); - expect(response.validators[0].pubkey).toBeDefined(); - expect(response.validators[0].votingPower).toBeGreaterThanOrEqual(0); - expect(response.validators[0].proposerPriority).toBeGreaterThanOrEqual(0); - - client.disconnect(); - }); - - it("can get all validators", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - const response = await client.validatorsAll(); - - expect(response).toBeTruthy(); - expect(response.blockHeight).toBeGreaterThanOrEqual(1); - expect(response.count).toBeGreaterThanOrEqual(1); - expect(response.total).toBeGreaterThanOrEqual(1); - expect(response.validators.length).toBeGreaterThanOrEqual(1); - expect(response.validators[0].address.length).toEqual(20); - expect(response.validators[0].pubkey).toBeDefined(); - expect(response.validators[0].votingPower).toBeGreaterThanOrEqual(0); - expect(response.validators[0].proposerPriority).toBeGreaterThanOrEqual(0); - - client.disconnect(); - }); - - it("can call a bunch of methods", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - expect(await client.block()).toBeTruthy(); - expect(await client.genesis()).toBeTruthy(); - expect(await client.health()).toBeNull(); - - client.disconnect(); - }); - - describe("status", () => { - it("works", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const status = await client.status(); - - // node info - expect(status.nodeInfo.version).toMatch(expected.version); - expect(status.nodeInfo.protocolVersion).toEqual({ - p2p: expected.p2pVersion, - block: expected.blockVersion, - app: expected.appVersion, - }); - expect(status.nodeInfo.network).toMatch(expected.chainId); - expect(status.nodeInfo.other.size).toBeGreaterThanOrEqual(2); - expect(status.nodeInfo.other.get("tx_index")).toEqual("on"); - - // sync info - expect(status.syncInfo.catchingUp).toEqual(false); - expect(status.syncInfo.latestBlockHeight).toBeGreaterThanOrEqual(1); - - // validator info - expect(status.validatorInfo.pubkey).toBeTruthy(); - expect(status.validatorInfo.votingPower).toBeGreaterThan(0); - - client.disconnect(); - }); - }); - - describe("numUnconfirmedTxs", () => { - it("works", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const response = await client.numUnconfirmedTxs(); - - expect(response.total).toBeGreaterThanOrEqual(0); - expect(response.totalBytes).toBeGreaterThanOrEqual(0); - - client.disconnect(); - }); - }); - - describe("blockResults", () => { - it("works", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const height = 3; - const results = await client.blockResults(height); - expect(results.height).toEqual(height); - expect(results.results).toEqual([]); - expect(results.beginBlockEvents).toEqual([]); - expect(results.endBlockEvents).toEqual([]); - - client.disconnect(); - }); - }); - - describe("blockSearch", () => { - beforeAll(async () => { - if (tendermintEnabled()) { - const client = await Tendermint35Client.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 Tendermint35Client.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 Tendermint35Client.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(); - const client = await Tendermint35Client.create(rpcFactory()); - - // Run in parallel to increase chance there is no block between the calls - const [status, blockchain] = await Promise.all([client.status(), client.blockchain()]); - const height = status.syncInfo.latestBlockHeight; - - expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(3); - expect(blockchain.blockMetas[0].header.height).toEqual(height); - expect(blockchain.blockMetas[1].header.height).toEqual(height - 1); - expect(blockchain.blockMetas[2].header.height).toEqual(height - 2); - - client.disconnect(); - }); - - it("can limit by maxHeight", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(undefined, height - 1); - expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(2); - expect(blockchain.blockMetas[0].header.height).toEqual(height - 1); // upper limit included - expect(blockchain.blockMetas[1].header.height).toEqual(height - 2); - - client.disconnect(); - }); - - it("works with maxHeight in the future", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(undefined, height + 20); - expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(3); - expect(blockchain.blockMetas[0].header.height).toEqual(blockchain.lastHeight); - expect(blockchain.blockMetas[1].header.height).toEqual(blockchain.lastHeight - 1); - expect(blockchain.blockMetas[2].header.height).toEqual(blockchain.lastHeight - 2); - - client.disconnect(); - }); - - it("can limit by minHeight and maxHeight", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(height - 2, height - 1); - expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height); - expect(blockchain.blockMetas.length).toEqual(2); - expect(blockchain.blockMetas[0].header.height).toEqual(height - 1); // upper limit included - expect(blockchain.blockMetas[1].header.height).toEqual(height - 2); // lower limit included - - client.disconnect(); - }); - - it("contains all the info", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(height - 1, height - 1); - - expect(blockchain.lastHeight).toBeGreaterThanOrEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(1); - const meta = blockchain.blockMetas[0]; - - expect(meta.blockId).toEqual(jasmine.objectContaining({})); - expect(meta.blockSize).toBeInstanceOf(Number); - expect(meta.header).toEqual( - jasmine.objectContaining({ - version: { - block: expected.blockVersion, - app: expected.appVersion, - }, - chainId: jasmine.stringMatching(expected.chainId), - }), - ); - expect(meta.numTxs).toBeInstanceOf(Number); - - client.disconnect(); - }); - }); - - describe("tx", () => { - it("can query a tx properly", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const find = randomString(); - const me = randomString(); - const tx = buildKvTx(find, me); - - const txRes = await client.broadcastTxCommit({ tx: tx }); - expect(responses.broadcastTxCommitSuccess(txRes)).toEqual(true); - expect(txRes.height).toBeTruthy(); - const height: number = txRes.height || 0; // || 0 for type system - expect(txRes.hash.length).not.toEqual(0); - const hash = txRes.hash; - - await tendermintSearchIndexUpdated(); - - // find by hash - does it match? - const r = await client.tx({ hash: hash, prove: true }); - // both values come from rpc, so same type (Buffer/Uint8Array) - expect(r.hash).toEqual(hash); - // force the type when comparing to locally generated value - expect(r.tx).toEqual(tx); - expect(r.height).toEqual(height); - expect(r.proof).toBeTruthy(); - - // txSearch - you must enable the indexer when running - // tendermint, else you get empty results - const query = buildQuery({ tags: [{ key: "app.key", value: find }] }); - - const s = await client.txSearch({ query: query, page: 1, per_page: 30 }); - // should find the tx - expect(s.totalCount).toEqual(1); - // should return same info as querying directly, - // except without the proof - expect(s.txs[0]).toEqual({ ...r, proof: undefined }); - - // ensure txSearchAll works as well - const sall = await client.txSearchAll({ query: query }); - // should find the tx - expect(sall.totalCount).toEqual(1); - // should return same info as querying directly, - // except without the proof - expect(sall.txs[0]).toEqual({ ...r, proof: undefined }); - - // and let's query the block itself to see this transaction - const block = await client.block(height); - expect(block.block.txs.length).toEqual(1); - expect(block.block.txs[0]).toEqual(tx); - - client.disconnect(); - }); - }); - - describe("txSearch", () => { - const key = randomString(); - - beforeAll(async () => { - if (tendermintEnabled()) { - const client = await Tendermint35Client.create(rpcFactory()); - - // eslint-disable-next-line no-inner-declarations - async function sendTx(): Promise { - const me = randomString(); - const tx = buildKvTx(key, me); - - 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("returns transactions in descending order by default", async () => { - // NOTE: The Tendermint docs states the default ordering is "desc". Until - // 0.35 it was actually "asc" but from 0.35 on it is "desc". - // Docs: https://docs.tendermint.com/master/rpc/#/Info/tx_search - // Code 0.34: https://github.com/tendermint/tendermint/blob/v0.34.10/rpc/core/tx.go#L89 - // Code 0.35: https://github.com/tendermint/tendermint/blob/v0.35.6/internal/rpc/core/tx.go#L93 - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - const result = await client.txSearch({ query: query }); - - expect(result.totalCount).toEqual(3); - result.txs.slice(1).reduce((lastHeight, { height }) => { - expect(height).toBeLessThanOrEqual(lastHeight); - return height; - }, result.txs[0].height); - - client.disconnect(); - }); - - it("can set the order", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - const result1 = await client.txSearch({ query: query, order_by: "desc" }); - const result2 = await client.txSearch({ query: query, order_by: "asc" }); - - expect(result1.totalCount).toEqual(result2.totalCount); - expect([...result1.txs].reverse()).toEqual(result2.txs); - - client.disconnect(); - }); - - it("can paginate over txSearch results", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - // expect one page of results - const s1 = await client.txSearch({ query: query, page: 1, per_page: 2 }); - expect(s1.totalCount).toEqual(3); - expect(s1.txs.length).toEqual(2); - - // second page - const s2 = await client.txSearch({ query: query, page: 2, per_page: 2 }); - expect(s2.totalCount).toEqual(3); - expect(s2.txs.length).toEqual(1); - - client.disconnect(); - }); - - it("can get all search results in one call", async () => { - pendingWithoutTendermint(); - const client = await Tendermint35Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - const sall = await client.txSearchAll({ query: query, per_page: 2 }); - expect(sall.totalCount).toEqual(3); - expect(sall.txs.length).toEqual(3); - // make sure there are in order from highest to lowest height - const [tx1, tx2, tx3] = sall.txs; - expect(tx2.height).toBeLessThan(tx1.height); - expect(tx3.height).toBeLessThan(tx2.height); - - client.disconnect(); - }); - }); -} - -function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues): void { - it("can subscribe to block header events", (done) => { - pendingWithoutTendermint(); - - const testStart = ReadonlyDate.now(); - - (async () => { - const events: responses.NewBlockHeaderEvent[] = []; - const client = await Tendermint35Client.create(rpcFactory()); - const stream = client.subscribeNewBlockHeader(); - expect(stream).toBeTruthy(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.chainId).toMatch(expected.chainId); - expect(event.height).toBeGreaterThan(0); - // seems that tendermint just guarantees within the last second for timestamp - expect(event.time.getTime()).toBeGreaterThan(testStart - 1000); - // Tendermint clock is sometimes ahead of test clock. Add 10ms tolerance - expect(event.time.getTime()).toBeLessThanOrEqual(ReadonlyDate.now() + 10); - expect(event.lastBlockId).toBeTruthy(); - - // merkle roots for proofs - expect(event.appHash).toBeTruthy(); - expect(event.consensusHash).toBeTruthy(); - expect(event.dataHash).toBeTruthy(); - expect(event.evidenceHash).toBeTruthy(); - expect(event.lastCommitHash).toBeTruthy(); - expect(event.lastResultsHash).toBeTruthy(); - expect(event.validatorsHash).toBeTruthy(); - - events.push(event); - - if (events.length === 2) { - subscription.unsubscribe(); - expect(events.length).toEqual(2); - expect(events[1].chainId).toEqual(events[0].chainId); - expect(events[1].height).toEqual(events[0].height + 1); - expect(events[1].time.getTime()).toBeGreaterThan(events[0].time.getTime()); - - expect(events[1].appHash).toEqual(events[0].appHash); - expect(events[1].consensusHash).toEqual(events[0].consensusHash); - expect(events[1].dataHash).toEqual(events[0].dataHash); - expect(events[1].evidenceHash).toEqual(events[0].evidenceHash); - expect(events[1].lastCommitHash).not.toEqual(events[0].lastCommitHash); - // This test is flaky. Not sure what to test here. - // expect(events[1].lastResultsHash).not.toEqual(events[0].lastResultsHash); - expect(events[1].validatorsHash).toEqual(events[0].validatorsHash); - - client.disconnect(); - done(); - } - }, - error: done.fail, - complete: () => done.fail("Stream completed before we are done"), - }); - })().catch(done.fail); - }); - - it("can subscribe to block events", async () => { - pendingWithoutTendermint(); - - const testStart = ReadonlyDate.now(); - - const transactionData1 = buildKvTx(randomString(), randomString()); - const transactionData2 = buildKvTx(randomString(), randomString()); - - const events: responses.NewBlockEvent[] = []; - const client = await Tendermint35Client.create(rpcFactory()); - const stream = client.subscribeNewBlock(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.header.chainId).toMatch(expected.chainId); - expect(event.header.height).toBeGreaterThan(0); - // seems that tendermint just guarantees within the last second for timestamp - expect(event.header.time.getTime()).toBeGreaterThan(testStart - 1000); - // Tendermint clock is sometimes ahead of test clock. Add 10ms tolerance - expect(event.header.time.getTime()).toBeLessThanOrEqual(ReadonlyDate.now() + 10); - expect(event.header.lastBlockId).toBeTruthy(); - - // merkle roots for proofs - expect(event.header.appHash).toBeTruthy(); - expect(event.header.consensusHash).toBeTruthy(); - expect(event.header.dataHash).toBeTruthy(); - expect(event.header.evidenceHash).toBeTruthy(); - expect(event.header.lastCommitHash).toBeTruthy(); - expect(event.header.lastResultsHash).toBeTruthy(); - expect(event.header.validatorsHash).toBeTruthy(); - - events.push(event); - }, - error: fail, - }); - - await client.broadcastTxCommit({ tx: transactionData1 }); - await client.broadcastTxCommit({ tx: transactionData2 }); - - // wait for events to be processed - await sleep(100); - - // Stop listening for new blocks - subscription.unsubscribe(); - - // We don't know exactly in which block the transactions are added. So we look into those - // with txs. - const eventsWithTx = events.filter((e) => e.txs.length > 0); - - expect(eventsWithTx.length).toEqual(2); - // Block body - expect(eventsWithTx[0].txs.length).toEqual(1); - expect(eventsWithTx[0].txs[0]).toEqual(transactionData1); - expect(eventsWithTx[1].txs.length).toEqual(1); - expect(eventsWithTx[1].txs[0]).toEqual(transactionData2); - // Block header - expect(eventsWithTx[1].header.height).toBeGreaterThan(eventsWithTx[0].header.height); - expect(eventsWithTx[1].header.chainId).toEqual(eventsWithTx[0].header.chainId); - expect(eventsWithTx[1].header.time.getTime()).toBeGreaterThan(eventsWithTx[0].header.time.getTime()); - expect(eventsWithTx[1].header.appHash).not.toEqual(eventsWithTx[0].header.appHash); - expect(eventsWithTx[1].header.validatorsHash).toEqual(eventsWithTx[0].header.validatorsHash); - - client.disconnect(); - }); - - it("can subscribe to transaction events", async () => { - pendingWithoutTendermint(); - - const events: responses.TxEvent[] = []; - const client = await Tendermint35Client.create(rpcFactory()); - const stream = client.subscribeTx(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.height).toBeGreaterThan(0); - expect(event.result).toBeTruthy(); - expect(event.result.events.length).toBeGreaterThanOrEqual(1); - - events.push(event); - - if (events.length === 2) { - subscription.unsubscribe(); - } - }, - error: fail, - }); - - const transactionData1 = buildKvTx(randomString(), randomString()); - const transactionData2 = buildKvTx(randomString(), randomString()); - - await client.broadcastTxCommit({ tx: transactionData1 }); - await client.broadcastTxCommit({ tx: transactionData2 }); - - // wait for events to be processed - await sleep(50); - - expect(events.length).toEqual(2); - // Meta - expect(events[1].height).toBeGreaterThan(events[0].height); - expect(events[1].result.events).not.toEqual(events[0].result.events); - // Content - expect(events[0].tx).toEqual(transactionData1); - expect(events[1].tx).toEqual(transactionData2); - - client.disconnect(); - }); - - it("can subscribe to transaction events filtered by creator", async () => { - pendingWithoutTendermint(); - - const transactionData1 = buildKvTx(randomString(), randomString()); - const transactionData2 = buildKvTx(randomString(), randomString()); - - const events: responses.TxEvent[] = []; - const client = await Tendermint35Client.create(rpcFactory()); - const query = buildQuery({ tags: [{ key: "app.creator", value: expected.appCreator }] }); - const stream = client.subscribeTx(query); - expect(stream).toBeTruthy(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.height).toBeGreaterThan(0); - expect(event.result).toBeTruthy(); - expect(event.result.events.length).toBeGreaterThanOrEqual(1); - events.push(event); - - if (events.length === 2) { - subscription.unsubscribe(); - } - }, - error: fail, - }); - - await client.broadcastTxCommit({ tx: transactionData1 }); - await client.broadcastTxCommit({ tx: transactionData2 }); - - // wait for events to be processed - await sleep(50); - - expect(events.length).toEqual(2); - // Meta - expect(events[1].height).toBeGreaterThan(events[0].height); - expect(events[1].result.events).not.toEqual(events[0].result.events); - // Content - expect(events[0].tx).toEqual(transactionData1); - expect(events[1].tx).toEqual(transactionData2); - - client.disconnect(); - }); - - it("can unsubscribe and re-subscribe to the same stream", async () => { - pendingWithoutTendermint(); - - const client = await Tendermint35Client.create(rpcFactory()); - const stream = client.subscribeNewBlockHeader(); - - const event1 = await firstEvent(stream); - expect(event1.height).toBeGreaterThanOrEqual(1); - expect(event1.time.getTime()).toBeGreaterThanOrEqual(1); - - // No sleep: producer will not be stopped in the meantime - - const event2 = await firstEvent(stream); - expect(event2.height).toBeGreaterThan(event1.height); - expect(event2.time.getTime()).toBeGreaterThan(event1.time.getTime()); - - // Very short sleep: just enough to schedule asynchronous producer stopping - await sleep(5); - - const event3 = await firstEvent(stream); - expect(event3.height).toBeGreaterThan(event2.height); - expect(event3.time.getTime()).toBeGreaterThan(event2.time.getTime()); - - // Proper sleep: enough to finish unsubscribing at over the network - await sleep(100); - - const event4 = await firstEvent(stream); - expect(event4.height).toBeGreaterThan(event3.height); - expect(event4.time.getTime()).toBeGreaterThan(event3.time.getTime()); - - client.disconnect(); - }); - - it("can subscribe twice", async () => { - pendingWithoutTendermint(); - - const client = await Tendermint35Client.create(rpcFactory()); - const stream1 = client.subscribeNewBlockHeader(); - const stream2 = client.subscribeNewBlockHeader(); - - const events = await toListPromise(Stream.merge(stream1, stream2), 4); - - expect(new Set(events.map((e) => e.height)).size).toEqual(2); - - client.disconnect(); - }); -} - -describe("Tendermint35Client", () => { - const { url, expected } = tendermintInstances[35]; - - it("can connect to a given url", async () => { - pendingWithoutTendermint(); - - // default connection - { - const client = await Tendermint35Client.connect(url); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - } - - // http connection - { - const client = await Tendermint35Client.connect("http://" + url); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - } - - // ws connection - { - const client = await Tendermint35Client.connect("ws://" + url); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - } - }); - - describe("With HttpClient", () => { - defaultTestSuite(() => new HttpClient(url), expected); - }); - - describe("With WebsocketClient", () => { - // don't print out WebSocket errors if marked pending - const onError = process.env.TENDERMINT_ENABLED ? console.error : () => 0; - const factory = (): WebsocketClient => new WebsocketClient(url, onError); - defaultTestSuite(factory, expected); - websocketTestSuite(factory, expected); - }); -}); diff --git a/packages/tendermint-rpc/src/tendermint35/tendermint35client.ts b/packages/tendermint-rpc/src/tendermint35/tendermint35client.ts deleted file mode 100644 index 108966203e..0000000000 --- a/packages/tendermint-rpc/src/tendermint35/tendermint35client.ts +++ /dev/null @@ -1,361 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Stream } from "xstream"; - -import { createJsonRpcRequest } from "../jsonrpc"; -import { - HttpClient, - HttpEndpoint, - instanceOfRpcStreamingClient, - RpcClient, - SubscriptionEvent, - WebsocketClient, -} from "../rpcclients"; -import { adaptor35, Decoder, Encoder, Params, Responses } from "./adaptor"; -import * as requests from "./requests"; -import * as responses from "./responses"; - -/** - * Please note the Tendermint 0.35 client is currently not exported and may break or be removed at any point in time. - * - * @see https://github.com/cosmos/cosmjs/issues/1225 - */ -export class Tendermint35Client { - /** - * Creates a new Tendermint client for the given endpoint. - * - * Uses HTTP when the URL schema is http or https. Uses WebSockets otherwise. - */ - public static async connect(endpoint: string | HttpEndpoint): Promise { - if (typeof endpoint === "object") { - return Tendermint35Client.create(new HttpClient(endpoint)); - } else { - const useHttp = endpoint.startsWith("http://") || endpoint.startsWith("https://"); - const rpcClient = useHttp ? new HttpClient(endpoint) : new WebsocketClient(endpoint); - return Tendermint35Client.create(rpcClient); - } - } - - /** - * Creates a new Tendermint client given an RPC client. - */ - public static async create(rpcClient: RpcClient): Promise { - // For some very strange reason I don't understand, tests start to fail on some systems - // (our CI) when skipping the status call before doing other queries. Sleeping a little - // while did not help. Thus we query the version as a way to say "hi" to the backend, - // even in cases where we don't use the result. - const _version = await this.detectVersion(rpcClient); - return new Tendermint35Client(rpcClient); - } - - private static async detectVersion(client: RpcClient): Promise { - const req = createJsonRpcRequest(requests.Method.Status); - const response = await client.execute(req); - const result = response.result; - - if (!result || !result.node_info) { - throw new Error("Unrecognized format for status response"); - } - - const version = result.node_info.version; - if (typeof version !== "string") { - throw new Error("Unrecognized version format: must be string"); - } - return version; - } - - private readonly client: RpcClient; - private readonly p: Params; - private readonly r: Responses; - - /** - * Use `Tendermint34Client.connect` or `Tendermint34Client.create` to create an instance. - */ - private constructor(client: RpcClient) { - this.client = client; - this.p = adaptor35.params; - this.r = adaptor35.responses; - } - - public disconnect(): void { - this.client.disconnect(); - } - - public async abciInfo(): Promise { - const query: requests.AbciInfoRequest = { method: requests.Method.AbciInfo }; - return this.doCall(query, this.p.encodeAbciInfo, this.r.decodeAbciInfo); - } - - public async abciQuery(params: requests.AbciQueryParams): Promise { - const query: requests.AbciQueryRequest = { params: params, method: requests.Method.AbciQuery }; - return this.doCall(query, this.p.encodeAbciQuery, this.r.decodeAbciQuery); - } - - public async block(height?: number): Promise { - const query: requests.BlockRequest = { method: requests.Method.Block, params: { height: height } }; - return this.doCall(query, this.p.encodeBlock, this.r.decodeBlock); - } - - public async blockResults(height?: number): Promise { - const query: requests.BlockResultsRequest = { - method: requests.Method.BlockResults, - params: { height: height }, - }; - 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. - * - * @param minHeight The minimum height to be included in the result. Defaults to 0. - * @param maxHeight The maximum height to be included in the result. Defaults to infinity. - */ - public async blockchain(minHeight?: number, maxHeight?: number): Promise { - const query: requests.BlockchainRequest = { - method: requests.Method.Blockchain, - params: { - minHeight: minHeight, - maxHeight: maxHeight, - }, - }; - return this.doCall(query, this.p.encodeBlockchain, this.r.decodeBlockchain); - } - - /** - * Broadcast transaction to mempool and wait for response - * - * @see https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync - */ - public async broadcastTxSync( - params: requests.BroadcastTxParams, - ): Promise { - const query: requests.BroadcastTxRequest = { params: params, method: requests.Method.BroadcastTxSync }; - return this.doCall(query, this.p.encodeBroadcastTx, this.r.decodeBroadcastTxSync); - } - - /** - * Broadcast transaction to mempool and do not wait for result - * - * @see https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async - */ - public async broadcastTxAsync( - params: requests.BroadcastTxParams, - ): Promise { - const query: requests.BroadcastTxRequest = { params: params, method: requests.Method.BroadcastTxAsync }; - return this.doCall(query, this.p.encodeBroadcastTx, this.r.decodeBroadcastTxAsync); - } - - /** - * Broadcast transaction to mempool and wait for block - * - * @see https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit - */ - public async broadcastTxCommit( - params: requests.BroadcastTxParams, - ): Promise { - const query: requests.BroadcastTxRequest = { params: params, method: requests.Method.BroadcastTxCommit }; - return this.doCall(query, this.p.encodeBroadcastTx, this.r.decodeBroadcastTxCommit); - } - - public async commit(height?: number): Promise { - const query: requests.CommitRequest = { method: requests.Method.Commit, params: { height: height } }; - return this.doCall(query, this.p.encodeCommit, this.r.decodeCommit); - } - - public async genesis(): Promise { - const query: requests.GenesisRequest = { method: requests.Method.Genesis }; - return this.doCall(query, this.p.encodeGenesis, this.r.decodeGenesis); - } - - public async health(): Promise { - const query: requests.HealthRequest = { method: requests.Method.Health }; - return this.doCall(query, this.p.encodeHealth, this.r.decodeHealth); - } - - public async numUnconfirmedTxs(): Promise { - const query: requests.NumUnconfirmedTxsRequest = { method: requests.Method.NumUnconfirmedTxs }; - return this.doCall(query, this.p.encodeNumUnconfirmedTxs, this.r.decodeNumUnconfirmedTxs); - } - - public async status(): Promise { - const query: requests.StatusRequest = { method: requests.Method.Status }; - return this.doCall(query, this.p.encodeStatus, this.r.decodeStatus); - } - - public subscribeNewBlock(): Stream { - const request: requests.SubscribeRequest = { - method: requests.Method.Subscribe, - query: { type: requests.SubscriptionEventType.NewBlock }, - }; - return this.subscribe(request, this.r.decodeNewBlockEvent); - } - - public subscribeNewBlockHeader(): Stream { - const request: requests.SubscribeRequest = { - method: requests.Method.Subscribe, - query: { type: requests.SubscriptionEventType.NewBlockHeader }, - }; - return this.subscribe(request, this.r.decodeNewBlockHeaderEvent); - } - - public subscribeTx(query?: string): Stream { - const request: requests.SubscribeRequest = { - method: requests.Method.Subscribe, - query: { - type: requests.SubscriptionEventType.Tx, - raw: query, - }, - }; - return this.subscribe(request, this.r.decodeTxEvent); - } - - /** - * Get a single transaction by hash - * - * @see https://docs.tendermint.com/master/rpc/#/Info/tx - */ - public async tx(params: requests.TxParams): Promise { - const query: requests.TxRequest = { params: params, method: requests.Method.Tx }; - return this.doCall(query, this.p.encodeTx, this.r.decodeTx); - } - - /** - * Search for transactions that are in a block - * - * @see https://docs.tendermint.com/master/rpc/#/Info/tx_search - */ - public async txSearch(params: requests.TxSearchParams): Promise { - const query: requests.TxSearchRequest = { params: params, method: requests.Method.TxSearch }; - return this.doCall(query, this.p.encodeTxSearch, this.r.decodeTxSearch); - } - - // this should paginate through all txSearch options to ensure it returns all results. - // starts with page 1 or whatever was provided (eg. to start on page 7) - public async txSearchAll(params: requests.TxSearchParams): Promise { - let page = params.page || 1; - const txs: responses.TxResponse[] = []; - let done = false; - - while (!done) { - const resp = await this.txSearch({ ...params, page: page }); - txs.push(...resp.txs); - if (txs.length < resp.totalCount) { - page++; - } else { - done = true; - } - } - - return { - totalCount: txs.length, - txs: txs, - }; - } - - public async validators(params: requests.ValidatorsParams): Promise { - const query: requests.ValidatorsRequest = { - method: requests.Method.Validators, - params: params, - }; - return this.doCall(query, this.p.encodeValidators, this.r.decodeValidators); - } - - public async validatorsAll(height?: number): Promise { - const validators: responses.Validator[] = []; - let page = 1; - let done = false; - let blockHeight = height; - - while (!done) { - const response = await this.validators({ - per_page: 50, - height: blockHeight, - page: page, - }); - validators.push(...response.validators); - blockHeight = blockHeight || response.blockHeight; - if (validators.length < response.total) { - page++; - } else { - done = true; - } - } - - return { - // NOTE: Default value is for type safety but this should always be set - blockHeight: blockHeight ?? 0, - count: validators.length, - total: validators.length, - validators: validators, - }; - } - - // doCall is a helper to handle the encode/call/decode logic - private async doCall( - request: T, - encode: Encoder, - decode: Decoder, - ): Promise { - const req = encode(request); - const result = await this.client.execute(req); - return decode(result); - } - - private subscribe(request: requests.SubscribeRequest, decode: (e: SubscriptionEvent) => T): Stream { - if (!instanceOfRpcStreamingClient(this.client)) { - throw new Error("This RPC client type cannot subscribe to events"); - } - - const req = this.p.encodeSubscribe(request); - const eventStream = this.client.listen(req); - return eventStream.map((event) => { - return decode(event); - }); - } -} diff --git a/packages/tendermint-rpc/src/testutil.spec.ts b/packages/tendermint-rpc/src/testutil.spec.ts index 8bfd2146d8..3b699c67b5 100644 --- a/packages/tendermint-rpc/src/testutil.spec.ts +++ b/packages/tendermint-rpc/src/testutil.spec.ts @@ -49,19 +49,6 @@ export const tendermintInstances = { appVersion: 1, }, }, - 35: { - url: "localhost:11135", - version: "0.35.x", - blockTime: 500, - expected: { - chainId: /^dockerchain$/, - version: /^$/, // Unfortunately we don't get info here - appCreator: "Cosmoshi Netowoko", - p2pVersion: 8, - blockVersion: 11, - appVersion: 1, - }, - }, 37: { url: "localhost:11137", version: "0.37.x", diff --git a/scripts/tendermint/all_start.sh b/scripts/tendermint/all_start.sh index 38aeaa0c69..5c691cd829 100755 --- a/scripts/tendermint/all_start.sh +++ b/scripts/tendermint/all_start.sh @@ -7,12 +7,10 @@ command -v shellcheck >/dev/null && shellcheck "$0" # - https://hub.docker.com/r/cometbft/cometbft/tags/ declare -a TM_IMAGES TM_IMAGES[34]="tendermint/tendermint:v0.34.19" -TM_IMAGES[35]="tendermint/tendermint:v0.35.6" TM_IMAGES[37]="cometbft/cometbft:v0.37.0-rc3" declare -a TM_ROOTS TM_ROOTS[34]="/tendermint" -TM_ROOTS[35]="/tendermint" TM_ROOTS[37]="/cometbft" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/scripts/tendermint/all_stop.sh b/scripts/tendermint/all_stop.sh index 46246195ce..a2988c4fb5 100755 --- a/scripts/tendermint/all_stop.sh +++ b/scripts/tendermint/all_stop.sh @@ -4,7 +4,7 @@ command -v shellcheck >/dev/null && shellcheck "$0" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -for KEY in 34 35 37; do +for KEY in 34 37; do export TENDERMINT_NAME="tendermint-$KEY" echo "Stopping $TENDERMINT_NAME ..."