From fe0718043129e742f24b01245b4bd8fb9ff69ee0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 22 Nov 2021 21:07:14 +0100 Subject: [PATCH 1/2] Remove Tendermint33Client and related symbols --- CHANGELOG.md | 2 + HACKING.md | 1 - packages/tendermint-rpc/src/index.ts | 2 - .../src/tendermint33/adaptor/index.ts | 13 - .../src/tendermint33/adaptor/requests.ts | 161 ---- .../src/tendermint33/adaptor/responses.ts | 838 ------------------ .../src/tendermint33/adaptor/types.ts | 58 -- .../src/tendermint33/encodings.ts | 153 ---- .../src/tendermint33/hasher.spec.ts | 10 +- .../tendermint-rpc/src/tendermint33/hasher.ts | 8 +- .../tendermint-rpc/src/tendermint33/index.ts | 1 - .../tendermint33/tendermint33client.spec.ts | 775 ---------------- .../src/tendermint33/tendermint33client.ts | 299 ------- .../tendermint34/tendermint34client.spec.ts | 2 +- packages/tendermint-rpc/src/testutil.spec.ts | 12 - scripts/tendermint/all_start.sh | 2 +- scripts/tendermint/all_stop.sh | 2 +- 17 files changed, 7 insertions(+), 2332 deletions(-) delete mode 100644 packages/tendermint-rpc/src/tendermint33/adaptor/index.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/adaptor/requests.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/adaptor/responses.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/adaptor/types.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/tendermint33client.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/tendermint33client.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b99f844..20b59cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,11 +21,13 @@ and this project adheres to adding fields to interfaces. ([#928]) - @cosmjs/stargate and @cosmjs/cosmwasm-stargate: Add simulation support ([#931]). +- @cosmjs/tendermint-rpc: Remove `Tendermint33Client` and related symbols. [#865]: https://github.com/cosmos/cosmjs/issues/865 [#897]: https://github.com/cosmos/cosmjs/issues/897 [#928]: https://github.com/cosmos/cosmjs/issues/928 [#931]: https://github.com/cosmos/cosmjs/pull/931 +[#709]: https://github.com/cosmos/cosmjs/issues/709 ## [0.26.5] - 2021-11-20 diff --git a/HACKING.md b/HACKING.md index cb9752d6..a8f96965 100644 --- a/HACKING.md +++ b/HACKING.md @@ -101,7 +101,6 @@ order to avoid conflicts. Here is an overview of the ports used: | 4444 | socketserver | @cosmjs/sockets tests | | 4445 | socketserver slow | @cosmjs/sockets tests | | 9090 | simapp gRPC | Manual Stargate debugging | -| 11133 | Tendermint 0.33 RPC | @cosmjs/tendermint-rpc tests | | 11134 | Tendermint 0.34 RPC | @cosmjs/tendermint-rpc tests | | 26658 | simapp Tendermint RPC | Stargate client tests | | 26659 | wasmd Tendermint RPC | @cosmjs/cosmwasm-stargate tests | diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index 4c741d23..8ca195e3 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -20,9 +20,7 @@ export { ValidatorSecp256k1Pubkey, ValidatorPubkey, } from "./types"; -export * as tendermint33 from "./tendermint33"; export { - Tendermint33Client, AbciInfoResponse, AbciQueryResponse, Attribute, diff --git a/packages/tendermint-rpc/src/tendermint33/adaptor/index.ts b/packages/tendermint-rpc/src/tendermint33/adaptor/index.ts deleted file mode 100644 index a9309502..00000000 --- a/packages/tendermint-rpc/src/tendermint33/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 adaptor33: Adaptor = { - params: Params, - responses: Responses, - hashTx: hashTx, - hashBlock: hashBlock, -}; diff --git a/packages/tendermint-rpc/src/tendermint33/adaptor/requests.ts b/packages/tendermint-rpc/src/tendermint33/adaptor/requests.ts deleted file mode 100644 index beb4b97c..00000000 --- a/packages/tendermint-rpc/src/tendermint33/adaptor/requests.ts +++ /dev/null @@ -1,161 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { toBase64, toHex } from "@cosmjs/encoding"; -import { JsonRpcRequest } from "@cosmjs/json-rpc"; - -import { createJsonRpcRequest } from "../../jsonrpc"; -import { assertNotEmpty, Integer, 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(Integer.encode, param.height), - }; -} - -interface RpcBlockchainRequestParams { - readonly minHeight?: string; - readonly maxHeight?: string; -} - -function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams { - return { - minHeight: may(Integer.encode, param.minHeight), - maxHeight: may(Integer.encode, param.maxHeight), - }; -} - -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(Integer.encode, 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 { - /** base64 encoded */ - readonly hash: string; - readonly prove?: boolean; -} -function encodeTxParams(params: requests.TxParams): RpcTxParams { - return { - hash: toBase64(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(Integer.encode, params.page), - per_page: may(Integer.encode, 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(Integer.encode, params.height), - page: may(Integer.encode, params.page), - per_page: may(Integer.encode, 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 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 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/tendermint33/adaptor/responses.ts b/packages/tendermint-rpc/src/tendermint33/adaptor/responses.ts deleted file mode 100644 index 8ed85c33..00000000 --- a/packages/tendermint-rpc/src/tendermint33/adaptor/responses.ts +++ /dev/null @@ -1,838 +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 { fromRfc3339WithNanoseconds } from "../../dates"; -import { SubscriptionEvent } from "../../rpcclients"; -import { BlockIdFlag, CommitSignature, ValidatorPubkey } from "../../types"; -import { - assertArray, - assertBoolean, - assertNotEmpty, - assertNumber, - assertObject, - assertSet, - assertString, - dictionaryToStringMap, - Integer, - may, - optional, -} 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(Integer.parse, 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 */ - readonly key: string; - /** base64 encoded */ - readonly value?: string; - readonly proofOps?: RpcQueryProof; - readonly height?: string; - readonly index?: string; - readonly code?: string; // only for errors - readonly log?: string; -} - -function decodeAbciQuery(data: RpcAbciQueryResponse): responses.AbciQueryResponse { - return { - key: fromBase64(optional(data.key, "")), - value: fromBase64(optional(data.value, "")), - proof: may(decodeQueryProof, data.proofOps), - height: may(Integer.parse, data.height), - code: may(Integer.parse, data.code), - index: may(Integer.parse, data.index), - log: data.log, - }; -} - -interface RpcAttribute { - /** base64 encoded */ - readonly key: string; - /** base64 encoded */ - readonly value: string; -} - -function decodeAttribute(attribute: RpcAttribute): responses.Attribute { - return { - key: fromBase64(assertNotEmpty(attribute.key)), - value: fromBase64(optional(attribute.value, "")), - }; -} - -function decodeAttributes(attributes: readonly RpcAttribute[]): responses.Attribute[] { - return assertArray(attributes).map(decodeAttribute); -} - -interface RpcEvent { - readonly type: string; - readonly attributes: readonly RpcAttribute[]; -} - -function decodeEvent(event: RpcEvent): responses.Event { - return { - type: event.type, - attributes: decodeAttributes(event.attributes), - }; -} - -function decodeEvents(events: readonly RpcEvent[] | undefined): readonly responses.Event[] { - return assertArray(events ?? []).map(decodeEvent); -} - -interface RpcTxData { - readonly code?: number; - readonly log?: string; - /** base64 encoded */ - readonly data?: string; - readonly events?: readonly RpcEvent[]; -} - -function decodeTxData(data: RpcTxData): responses.TxData { - return { - data: may(fromBase64, data.data), - log: data.log, - code: Integer.parse(assertNumber(optional(data.code, 0))), - events: decodeEvents(data.events), - }; -} - -// yes, a different format for status and dump consensus state -interface RpcPubkey { - readonly type: string; - /** base64 encoded */ - readonly value: string; -} - -function decodePubkey(data: RpcPubkey): ValidatorPubkey { - 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}`); - } -} - -// for evidence, block results, etc. -interface RpcValidatorUpdate { - /** hex encoded */ - readonly address: string; - readonly pub_key: RpcPubkey; - readonly voting_power: string; - readonly proposer_priority: string; -} - -function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.Validator { - return { - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: Integer.parse(assertNotEmpty(data.voting_power)), - address: fromHex(assertNotEmpty(data.address)), - proposerPriority: Integer.parse(data.proposer_priority), - }; -} - -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: Integer.parse(assertNotEmpty(data.max_bytes)), - maxGas: Integer.parse(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: Integer.parse(assertNotEmpty(data.max_age_num_blocks)), - maxAgeDuration: Integer.parse(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)), - }; -} - -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: Integer.parse(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: string; - /** hex encoded */ - readonly hash: string; - }; -} - -function decodeBlockId(data: RpcBlockId): responses.BlockId { - return { - hash: fromHex(assertNotEmpty(data.hash)), - parts: { - total: Integer.parse(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: Integer.parse(data.block), - app: Integer.parse(data.app ?? 0), - }; -} - -interface RpcHeader { - readonly version: RpcBlockVersion; - readonly chain_id: string; - readonly height: string; - readonly time: string; - readonly num_txs: string; - readonly total_txs: 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: Integer.parse(assertNotEmpty(data.height)), - time: fromRfc3339WithNanoseconds(assertNotEmpty(data.time)), - - lastBlockId: decodeBlockId(data.last_block_id), - - lastCommitHash: fromHex(assertNotEmpty(data.last_commit_hash)), - dataHash: fromHex(assertSet(data.data_hash)), - - validatorsHash: fromHex(assertNotEmpty(data.validators_hash)), - nextValidatorsHash: fromHex(assertNotEmpty(data.next_validators_hash)), - consensusHash: fromHex(assertNotEmpty(data.consensus_hash)), - appHash: fromHex(assertNotEmpty(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 header: RpcHeader; -} - -function decodeBlockMeta(data: RpcBlockMeta): responses.BlockMeta { - return { - blockId: decodeBlockId(data.block_id), - header: decodeHeader(data.header), - }; -} - -interface RpcBlockchainResponse { - readonly last_height: string; - readonly block_metas: readonly RpcBlockMeta[]; -} - -function decodeBlockchain(data: RpcBlockchainResponse): responses.BlockchainResponse { - return { - lastHeight: Integer.parse(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: Integer.parse(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; - /** bae64 encoded */ - readonly signature: string; -}; - -function decodeCommitSignature(data: RpcSignature): CommitSignature { - return { - blockIdFlag: decodeBlockIdFlag(data.block_id_flag), - validatorAddress: fromHex(data.validator_address), - timestamp: fromRfc3339WithNanoseconds(assertNotEmpty(data.timestamp)), - signature: fromBase64(assertNotEmpty(data.signature)), - }; -} - -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: Integer.parse(assertNotEmpty(data.height)), - round: Integer.parse(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; -} - -function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Validator { - return { - address: fromHex(assertNotEmpty(data.address)), - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: Integer.parse(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; -} - -function decodeValidatorInfo(data: RpcValidatorInfo): responses.Validator { - return { - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: Integer.parse(assertNotEmpty(data.voting_power)), - address: fromHex(assertNotEmpty(data.address)), - }; -} - -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: Integer.parse(assertNotEmpty(data.protocol_version.app)), - block: Integer.parse(assertNotEmpty(data.protocol_version.block)), - p2p: Integer.parse(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: Integer.parse(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: Integer.parse(assertNotEmpty(data.proof.total)), - index: Integer.parse(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: Integer.parse(assertNotEmpty(data.height)), - index: Integer.parse(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: Integer.parse(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; - /** Not set since Tendermint 0.34 */ - readonly index?: number; -} - -function decodeTxEvent(data: RpcTxEvent): responses.TxEvent { - const tx = fromBase64(assertNotEmpty(data.tx)); - return { - tx: tx, - hash: hashTx(tx), - result: decodeTxData(data.result), - height: Integer.parse(assertNotEmpty(data.height)), - index: may(Integer.parse, data.index), - }; -} - -interface RpcValidatorsResponse { - readonly block_height: string; - readonly validators: readonly RpcValidatorUpdate[]; - readonly count: string; - readonly total: string; -} - -function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse { - return { - blockHeight: Integer.parse(assertNotEmpty(data.block_height)), - validators: assertArray(data.validators).map(decodeValidatorUpdate), - count: Integer.parse(assertNotEmpty(data.count)), - total: Integer.parse(assertNotEmpty(data.total)), - }; -} - -interface RpcEvidence { - readonly type: string; - readonly validator: RpcValidatorUpdate; - readonly height: string; - readonly time: string; - readonly totalVotingPower: string; -} - -function decodeEvidence(data: RpcEvidence): responses.Evidence { - return { - type: assertNotEmpty(data.type), - height: Integer.parse(assertNotEmpty(data.height)), - time: Integer.parse(assertNotEmpty(data.time)), - totalVotingPower: Integer.parse(assertNotEmpty(data.totalVotingPower)), - validator: decodeValidatorUpdate(data.validator), - }; -} - -function decodeEvidences(ev: readonly RpcEvidence[]): readonly responses.Evidence[] { - return assertArray(ev).map(decodeEvidence); -} - -interface RpcBlock { - readonly header: RpcHeader; - readonly last_commit: RpcCommit; - readonly data: { - /** Raw tx bytes, base64 encoded */ - readonly txs?: readonly string[]; - }; - readonly evidence?: { - readonly evidence?: readonly RpcEvidence[]; - }; -} - -function decodeBlock(data: RpcBlock): responses.Block { - return { - header: decodeHeader(assertObject(data.header)), - lastCommit: decodeCommit(assertObject(data.last_commit)), - txs: data.data.txs ? assertArray(data.data.txs).map(fromBase64) : [], - evidence: data.evidence && may(decodeEvidences, 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), - }; -} - -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 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 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/tendermint33/adaptor/types.ts b/packages/tendermint-rpc/src/tendermint33/adaptor/types.ts deleted file mode 100644 index 9ecd4ab4..00000000 --- a/packages/tendermint-rpc/src/tendermint33/adaptor/types.ts +++ /dev/null @@ -1,58 +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 encodeBroadcastTx: (req: requests.BroadcastTxRequest) => JsonRpcRequest; - readonly encodeCommit: (req: requests.CommitRequest) => JsonRpcRequest; - readonly encodeGenesis: (req: requests.GenesisRequest) => JsonRpcRequest; - readonly encodeHealth: (req: requests.HealthRequest) => 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 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 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/tendermint33/encodings.ts b/packages/tendermint-rpc/src/tendermint33/encodings.ts index bbdcfc2a..b3a9768f 100644 --- a/packages/tendermint-rpc/src/tendermint33/encodings.ts +++ b/packages/tendermint-rpc/src/tendermint33/encodings.ts @@ -1,161 +1,8 @@ import { toUtf8 } from "@cosmjs/encoding"; -import { Int53 } from "@cosmjs/math"; 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; -} - -// optional uses the value or provides a default -export function optional(value: T | null | undefined, fallback: T): T { - return value === undefined || value === null ? fallback : 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; -} - -export class Integer { - public static parse(input: string | number): number { - const asInt = typeof input === "number" ? new Int53(input) : Int53.fromString(input); - return asInt.toNumber(); - } - - public static encode(num: number): string { - return new Int53(num).toString(); - } -} - // 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/ diff --git a/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts b/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts index c6644875..7ddc30e6 100644 --- a/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts +++ b/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts @@ -2,17 +2,9 @@ import { fromBase64, fromHex } from "@cosmjs/encoding"; import { ReadonlyDate } from "readonly-date"; import { ReadonlyDateWithNanoseconds } from "../dates"; -import { hashBlock, hashTx } from "./hasher"; +import { hashBlock } 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" diff --git a/packages/tendermint-rpc/src/tendermint33/hasher.ts b/packages/tendermint-rpc/src/tendermint33/hasher.ts index 75d9f484..e61ff04d 100644 --- a/packages/tendermint-rpc/src/tendermint33/hasher.ts +++ b/packages/tendermint-rpc/src/tendermint33/hasher.ts @@ -1,14 +1,8 @@ -import { Sha256, sha256 } from "@cosmjs/crypto"; +import { Sha256 } from "@cosmjs/crypto"; import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, 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)); diff --git a/packages/tendermint-rpc/src/tendermint33/index.ts b/packages/tendermint-rpc/src/tendermint33/index.ts index 979eed37..718ca926 100644 --- a/packages/tendermint-rpc/src/tendermint33/index.ts +++ b/packages/tendermint-rpc/src/tendermint33/index.ts @@ -1,7 +1,6 @@ // Note: all exports in this module are public available via // `import { tendermint33 } from "@cosmjs/tendermint-rpc"` -export { Tendermint33Client } from "./tendermint33client"; export { AbciInfoRequest, AbciQueryParams, diff --git a/packages/tendermint-rpc/src/tendermint33/tendermint33client.spec.ts b/packages/tendermint-rpc/src/tendermint33/tendermint33client.spec.ts deleted file mode 100644 index fee2769a..00000000 --- a/packages/tendermint-rpc/src/tendermint33/tendermint33client.spec.ts +++ /dev/null @@ -1,775 +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, - chainIdMatcher, - ExpectedValues, - pendingWithoutTendermint, - randomString, - tendermintEnabled, - tendermintInstances, - tendermintSearchIndexUpdated, -} from "../testutil.spec"; -import { adaptor33 } from "./adaptor"; -import { buildQuery } from "./requests"; -import * as responses from "./responses"; -import { Tendermint33Client } from "./tendermint33client"; - -function defaultTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValues): void { - describe("create", () => { - it("can auto-discover Tendermint version and communicate", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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 Tendermint33Client.create(rpcFactory()); - expect(await client.abciInfo()).toBeTruthy(); - client.disconnect(); - }); - }); - - it("can get genesis", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - const genesis = await client.genesis(); - expect(genesis).toBeTruthy(); - client.disconnect(); - }); - - describe("broadcastTxCommit", () => { - it("can broadcast a transaction", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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 Tendermint33Client.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(); - - client.disconnect(); - }); - }); - - describe("broadcastTxAsync", () => { - it("can broadcast a transaction", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - - const response = await client.broadcastTxAsync({ tx: tx }); - // TODO: Remove any cast after https://github.com/cosmos/cosmjs/issues/938 - expect((response as any).hash.length).toEqual(32); - - client.disconnect(); - }); - }); - - it("gets the same tx hash from backend as calculated locally", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - const tx = buildKvTx(randomString(), randomString()); - const calculatedTxHash = adaptor33.hashTx(tx); - - const response = await client.broadcastTxCommit({ tx: tx }); - expect(response.hash).toEqual(calculatedTxHash); - - client.disconnect(); - }); - - it("can query the state", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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).toBeFalsy(); - - client.disconnect(); - }); - - it("can get a commit", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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 Tendermint33Client.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 Tendermint33Client.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 Tendermint33Client.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 Tendermint33Client.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(chainIdMatcher); - 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("blockResults", () => { - it("works", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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("blockchain", () => { - it("returns latest in descending order by default", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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).toEqual(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 Tendermint33Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(undefined, height - 1); - expect(blockchain.lastHeight).toEqual(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 Tendermint33Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(undefined, height + 20); - expect(blockchain.lastHeight).toEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(2); - expect(blockchain.blockMetas[0].header.height).toEqual(height); - expect(blockchain.blockMetas[1].header.height).toEqual(height - 1); - - client.disconnect(); - }); - - it("can limit by minHeight and maxHeight", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(height - 2, height - 1); - expect(blockchain.lastHeight).toEqual(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 Tendermint33Client.create(rpcFactory()); - - const height = (await client.status()).syncInfo.latestBlockHeight; - const blockchain = await client.blockchain(height - 1, height - 1); - - expect(blockchain.lastHeight).toEqual(height); - expect(blockchain.blockMetas.length).toBeGreaterThanOrEqual(1); - const meta = blockchain.blockMetas[0]; - - // TODO: check all the fields - expect(meta).toEqual({ - blockId: jasmine.objectContaining({}), - // block_size: jasmine.stringMatching(nonNegativeIntegerMatcher), - // num_txs: jasmine.stringMatching(nonNegativeIntegerMatcher), - header: jasmine.objectContaining({ - version: { - block: expected.blockVersion, - app: expected.appVersion, - }, - chainId: jasmine.stringMatching(chainIdMatcher), - }), - }); - - client.disconnect(); - }); - }); - - describe("tx", () => { - it("can query a tx properly", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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 Tendermint33Client.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 ascending order by default", async () => { - // NOTE: The Tendermint docs claim the default ordering is "desc" but it is actually "asc" - // Docs: https://docs.tendermint.com/master/rpc/#/Info/tx_search - // Code: https://github.com/tendermint/tendermint/blob/v0.33.9/rpc/core/tx.go#L84 - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - const s = await client.txSearch({ query: query }); - - expect(s.totalCount).toEqual(3); - s.txs.slice(1).reduce((lastHeight, { height }) => { - expect(height).toBeGreaterThanOrEqual(lastHeight); - return height; - }, s.txs[0].height); - - client.disconnect(); - }); - - it("can set the order", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.create(rpcFactory()); - - const query = buildQuery({ tags: [{ key: "app.key", value: key }] }); - - const s1 = await client.txSearch({ query: query, order_by: "desc" }); - const s2 = await client.txSearch({ query: query, order_by: "asc" }); - - expect(s1.totalCount).toEqual(s2.totalCount); - expect([...s1.txs].reverse()).toEqual(s2.txs); - - client.disconnect(); - }); - - it("can paginate over txSearch results", async () => { - pendingWithoutTendermint(); - const client = await Tendermint33Client.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 Tendermint33Client.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 lowest to highest height - const [tx1, tx2, tx3] = sall.txs; - expect(tx2.height).toEqual(tx1.height + 1); - expect(tx3.height).toEqual(tx2.height + 1); - - 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 Tendermint33Client.create(rpcFactory()); - const stream = client.subscribeNewBlockHeader(); - expect(stream).toBeTruthy(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.chainId).toMatch(chainIdMatcher); - 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); - 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 Tendermint33Client.create(rpcFactory()); - const stream = client.subscribeNewBlock(); - const subscription = stream.subscribe({ - next: (event) => { - expect(event.header.chainId).toMatch(chainIdMatcher); - 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); - - 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(100); - - expect(events.length).toEqual(2); - // Block header - expect(events[1].header.height).toEqual(events[0].header.height + 1); - expect(events[1].header.chainId).toEqual(events[0].header.chainId); - expect(events[1].header.time.getTime()).toBeGreaterThan(events[0].header.time.getTime()); - expect(events[1].header.appHash).not.toEqual(events[0].header.appHash); - expect(events[1].header.validatorsHash).toEqual(events[0].header.validatorsHash); - // Block body - expect(events[0].txs.length).toEqual(1); - expect(events[1].txs.length).toEqual(1); - expect(events[0].txs[0]).toEqual(transactionData1); - expect(events[1].txs[0]).toEqual(transactionData2); - - client.disconnect(); - }); - - it("can subscribe to transaction events", async () => { - pendingWithoutTendermint(); - - const events: responses.TxEvent[] = []; - const client = await Tendermint33Client.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(100); - - expect(events.length).toEqual(2); - // Meta - expect(events[1].height).toEqual(events[0].height + 1); - 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 Tendermint33Client.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(100); - - expect(events.length).toEqual(2); - // Meta - expect(events[1].height).toEqual(events[0].height + 1); - 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 Tendermint33Client.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 Tendermint33Client.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(`Tendermint33Client`, () => { - const { url, expected } = tendermintInstances[0]; - - it("can connect to a given url", async () => { - pendingWithoutTendermint(); - - // default connection - { - const client = await Tendermint33Client.connect(url); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - } - - // http connection - { - const client = await Tendermint33Client.connect("http://" + url); - const info = await client.abciInfo(); - expect(info).toBeTruthy(); - client.disconnect(); - } - - // ws connection - { - const client = await Tendermint33Client.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/tendermint33/tendermint33client.ts b/packages/tendermint-rpc/src/tendermint33/tendermint33client.ts deleted file mode 100644 index 6ef2d594..00000000 --- a/packages/tendermint-rpc/src/tendermint33/tendermint33client.ts +++ /dev/null @@ -1,299 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import { Stream } from "xstream"; - -import { createJsonRpcRequest } from "../jsonrpc"; -import { - HttpClient, - instanceOfRpcStreamingClient, - RpcClient, - SubscriptionEvent, - WebsocketClient, -} from "../rpcclients"; -import { adaptor33, Decoder, Encoder, Params, Responses } from "./adaptor"; -import * as requests from "./requests"; -import * as responses from "./responses"; - -export class Tendermint33Client { - /** - * 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(url: string): Promise { - const useHttp = url.startsWith("http://") || url.startsWith("https://"); - const rpcClient = useHttp ? new HttpClient(url) : new WebsocketClient(url); - return Tendermint33Client.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 Tendermint33Client(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 `Tendermint33Client.connect` or `Tendermint33Client.create` to create an instance. - */ - private constructor(client: RpcClient) { - this.client = client; - this.p = adaptor33.params; - this.r = adaptor33.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); - } - - /** - * 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 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/tendermint34/tendermint34client.spec.ts b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts index 58a099ac..584f7c00 100644 --- a/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts +++ b/packages/tendermint-rpc/src/tendermint34/tendermint34client.spec.ts @@ -794,7 +794,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, expected: ExpectedValue } describe("Tendermint34Client", () => { - const { url, expected } = tendermintInstances[1]; + const { url, expected } = tendermintInstances[0]; it("can connect to a given url", async () => { pendingWithoutTendermint(); diff --git a/packages/tendermint-rpc/src/testutil.spec.ts b/packages/tendermint-rpc/src/testutil.spec.ts index 353dd26f..2b45d549 100644 --- a/packages/tendermint-rpc/src/testutil.spec.ts +++ b/packages/tendermint-rpc/src/testutil.spec.ts @@ -35,18 +35,6 @@ export interface TendermintInstance { * docker container kill */ export const tendermintInstances: readonly TendermintInstance[] = [ - { - url: "localhost:11133", - version: "0.33.x", - blockTime: 1000, - expected: { - version: "0.33.8", - appCreator: "Cosmoshi Netowoko", - p2pVersion: 7, - blockVersion: 10, - appVersion: 1, - }, - }, { url: "localhost:11134", version: "0.34.x", diff --git a/scripts/tendermint/all_start.sh b/scripts/tendermint/all_start.sh index 37a87fa8..00dff73d 100755 --- a/scripts/tendermint/all_start.sh +++ b/scripts/tendermint/all_start.sh @@ -4,7 +4,7 @@ command -v shellcheck >/dev/null && shellcheck "$0" # Find latest patch releases at https://hub.docker.com/r/tendermint/tendermint/tags/ declare -a TM_VERSIONS -TM_VERSIONS[33]=v0.33.8 +# TM_VERSIONS[33]=v0.33.8 TM_VERSIONS[34]=v0.34.10 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/scripts/tendermint/all_stop.sh b/scripts/tendermint/all_stop.sh index e77084df..e65e4416 100755 --- a/scripts/tendermint/all_stop.sh +++ b/scripts/tendermint/all_stop.sh @@ -3,7 +3,7 @@ set -o errexit -o nounset -o pipefail command -v shellcheck >/dev/null && shellcheck "$0" declare -a TM_VERSIONS -TM_VERSIONS[33]=v0.33.8 +# TM_VERSIONS[33]=v0.33.8 TM_VERSIONS[34]=v0.34.10 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" From 95e6156d3b7675f67ae8e7fc9a274e27fbaed3fa Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 22 Nov 2021 21:13:43 +0100 Subject: [PATCH 2/2] Use symbols from tendermint34 folder --- packages/tendermint-rpc/src/index.ts | 2 +- .../src/tendermint33/encodings.spec.ts | 90 ----- .../src/tendermint33/encodings.ts | 57 --- .../src/tendermint33/hasher.spec.ts | 83 ----- .../tendermint-rpc/src/tendermint33/hasher.ts | 61 ---- .../tendermint-rpc/src/tendermint33/index.ts | 73 ---- .../src/tendermint33/requests.spec.ts | 41 --- .../src/tendermint33/requests.ts | 185 ---------- .../src/tendermint33/responses.ts | 337 ------------------ 9 files changed, 1 insertion(+), 928 deletions(-) delete mode 100644 packages/tendermint-rpc/src/tendermint33/encodings.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/encodings.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/hasher.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/hasher.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/index.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/requests.spec.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/requests.ts delete mode 100644 packages/tendermint-rpc/src/tendermint33/responses.ts diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index 8ca195e3..e0ea8f6b 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -87,6 +87,6 @@ export { TxSearchRequest, ValidatorsRequest, ValidatorsParams, -} from "./tendermint33"; +} from "./tendermint34"; export * as tendermint34 from "./tendermint34"; export { Tendermint34Client } from "./tendermint34"; diff --git a/packages/tendermint-rpc/src/tendermint33/encodings.spec.ts b/packages/tendermint-rpc/src/tendermint33/encodings.spec.ts deleted file mode 100644 index afea0600..00000000 --- a/packages/tendermint-rpc/src/tendermint33/encodings.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { ReadonlyDate } from "readonly-date"; - -import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, 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("encodeInt", () => { - it("works", () => { - expect(encodeInt(0)).toEqual(Uint8Array.from([0])); - expect(encodeInt(1)).toEqual(Uint8Array.from([1])); - expect(encodeInt(127)).toEqual(Uint8Array.from([127])); - expect(encodeInt(128)).toEqual(Uint8Array.from([128, 1])); - expect(encodeInt(255)).toEqual(Uint8Array.from([255, 1])); - expect(encodeInt(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/tendermint33/encodings.ts b/packages/tendermint-rpc/src/tendermint33/encodings.ts deleted file mode 100644 index b3a9768f..00000000 --- a/packages/tendermint-rpc/src/tendermint33/encodings.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { toUtf8 } from "@cosmjs/encoding"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { BlockId, Version } from "./responses"; - -// 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 encodeInt(n: number): Uint8Array { - // eslint-disable-next-line no-bitwise - return n >= 0x80 ? Uint8Array.from([(n & 0xff) | 0x80, ...encodeInt(n >> 7)]) : 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, ...encodeInt(seconds)] : new Uint8Array(); - const nanoseconds = (time.nanoseconds || 0) + (milliseconds % 1000) * 1e6; - const nanosecondsArray = nanoseconds ? [0x10, ...encodeInt(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, ...encodeInt(version.block)]) : new Uint8Array(); - const appArray = version.app ? Uint8Array.from([0x10, ...encodeInt(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/tendermint33/hasher.spec.ts b/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts deleted file mode 100644 index 7ddc30e6..00000000 --- a/packages/tendermint-rpc/src/tendermint33/hasher.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { fromBase64, fromHex } from "@cosmjs/encoding"; -import { ReadonlyDate } from "readonly-date"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { hashBlock } from "./hasher"; - -describe("Hasher", () => { - 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/tendermint33/hasher.ts b/packages/tendermint-rpc/src/tendermint33/hasher.ts deleted file mode 100644 index e61ff04d..00000000 --- a/packages/tendermint-rpc/src/tendermint33/hasher.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Sha256 } from "@cosmjs/crypto"; - -import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings"; -import { Header } from "./responses"; - -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 { - const encodedFields: readonly Uint8Array[] = [ - encodeVersion(header.version), - encodeString(header.chainId), - encodeInt(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/tendermint33/index.ts b/packages/tendermint-rpc/src/tendermint33/index.ts deleted file mode 100644 index 718ca926..00000000 --- a/packages/tendermint-rpc/src/tendermint33/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Note: all exports in this module are public available via -// `import { tendermint33 } from "@cosmjs/tendermint-rpc"` - -export { - AbciInfoRequest, - AbciQueryParams, - AbciQueryRequest, - BlockRequest, - BlockchainRequest, - BlockResultsRequest, - BroadcastTxRequest, - BroadcastTxParams, - CommitRequest, - GenesisRequest, - HealthRequest, - Method, - Request, - QueryTag, - StatusRequest, - SubscriptionEventType, - TxParams, - TxRequest, - TxSearchParams, - TxSearchRequest, - ValidatorsRequest, - ValidatorsParams, -} from "./requests"; -export { - AbciInfoResponse, - AbciQueryResponse, - Attribute, - Block, - BlockchainResponse, - BlockGossipParams, - BlockId, - BlockMeta, - BlockParams, - BlockResponse, - BlockResultsResponse, - BroadcastTxAsyncResponse, - BroadcastTxCommitResponse, - broadcastTxCommitSuccess, - BroadcastTxSyncResponse, - broadcastTxSyncSuccess, - Commit, - CommitResponse, - ConsensusParams, - Event, - Evidence, - EvidenceParams, - GenesisResponse, - Header, - HealthResponse, - NewBlockEvent, - NewBlockHeaderEvent, - NodeInfo, - ProofOp, - QueryProof, - Response, - StatusResponse, - SyncInfo, - TxData, - TxEvent, - TxProof, - TxResponse, - TxSearchResponse, - TxSizeParams, - Validator, - ValidatorsResponse, - Version, - Vote, - VoteType, -} from "./responses"; diff --git a/packages/tendermint-rpc/src/tendermint33/requests.spec.ts b/packages/tendermint-rpc/src/tendermint33/requests.spec.ts deleted file mode 100644 index f2134ea8..00000000 --- a/packages/tendermint-rpc/src/tendermint33/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/tendermint33/requests.ts b/packages/tendermint-rpc/src/tendermint33/requests.ts deleted file mode 100644 index db8ed876..00000000 --- a/packages/tendermint-rpc/src/tendermint33/requests.ts +++ /dev/null @@ -1,185 +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", - BroadcastTxAsync = "broadcast_tx_async", - BroadcastTxSync = "broadcast_tx_sync", - BroadcastTxCommit = "broadcast_tx_commit", - Commit = "commit", - Genesis = "genesis", - Health = "health", - Status = "status", - Subscribe = "subscribe", - Tx = "tx", - TxSearch = "tx_search", - Validators = "validators", - Unsubscribe = "unsubscribe", -} - -export type Request = - | AbciInfoRequest - | AbciQueryRequest - | BlockRequest - | BlockchainRequest - | BlockResultsRequest - | BroadcastTxRequest - | CommitRequest - | GenesisRequest - | HealthRequest - | 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 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 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/tendermint33/responses.ts b/packages/tendermint-rpc/src/tendermint33/responses.ts deleted file mode 100644 index ee8accb7..00000000 --- a/packages/tendermint-rpc/src/tendermint33/responses.ts +++ /dev/null @@ -1,337 +0,0 @@ -import { ReadonlyDate } from "readonly-date"; - -import { ReadonlyDateWithNanoseconds } from "../dates"; -import { CommitSignature, ValidatorPubkey } from "../types"; - -export type Response = - | AbciInfoResponse - | AbciQueryResponse - | BlockResponse - | BlockResultsResponse - | BlockchainResponse - | BroadcastTxAsyncResponse - | BroadcastTxSyncResponse - | BroadcastTxCommitResponse - | CommitResponse - | GenesisResponse - | HealthResponse - | 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 log?: string; -} - -export interface BlockResponse { - readonly blockId: BlockId; - readonly block: Block; -} - -export interface BlockResultsResponse { - readonly height: number; - readonly results: readonly TxData[]; - readonly validatorUpdates: readonly Validator[]; - readonly consensusUpdates?: ConsensusParams; - readonly beginBlockEvents: readonly Event[]; - readonly endBlockEvents: readonly Event[]; -} - -export interface BlockchainResponse { - readonly lastHeight: number; - readonly blockMetas: readonly BlockMeta[]; -} - -/** No data in here because RPC method BroadcastTxAsync "returns right away, with no response" */ -export interface BroadcastTxAsyncResponse {} - -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 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; - /** @deprecated this value is not set in Tendermint 0.34+ */ - readonly index?: number; - readonly result: TxData; -} - -// Helper items used above - -/** An event attribute */ -export interface Attribute { - readonly key: Uint8Array; - readonly value: Uint8Array; -} - -export interface Event { - readonly type: string; - readonly attributes: readonly Attribute[]; -} - -export interface TxData { - readonly code: number; - readonly log?: string; - readonly data?: Uint8Array; - readonly events: readonly Event[]; - // readonly fees?: any; -} - -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 header: Header; - // TODO: Add blockSize (e.g "block_size": "471") - // TODO: Add numTxs (e.g "num_txs": "0") -} - -export interface BlockId { - readonly hash: Uint8Array; - readonly parts: { - readonly total: number; - readonly hash: Uint8Array; - }; -} - -export interface Block { - readonly header: Header; - readonly lastCommit: Commit; - readonly txs: readonly Uint8Array[]; - readonly evidence?: readonly Evidence[]; -} - -export interface Evidence { - readonly type: string; - readonly validator: Validator; - readonly height: number; - readonly time: number; - readonly totalVotingPower: number; -} - -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; - - // prev block info - readonly lastBlockId: BlockId; - - // hashes of block data - readonly lastCommitHash: Uint8Array; - readonly dataHash: Uint8Array; // empty when number of transaction is 0 - - // hashes from the app output from the prev block - readonly validatorsHash: Uint8Array; - readonly nextValidatorsHash: Uint8Array; - readonly consensusHash: Uint8Array; - readonly appHash: Uint8Array; - readonly lastResultsHash: Uint8Array; - - // consensus info - readonly evidenceHash: Uint8Array; - readonly proposerAddress: Uint8Array; -} - -export interface NodeInfo { - readonly id: Uint8Array; - /** IP and port */ - readonly listenAddr: string; - readonly network: string; - 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: number; - readonly proposerPriority?: number; -} - -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; -}