From 9ba93ebc9e8ef036896a70012056d1308f41ac2e Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 23 Jun 2020 17:01:45 +0200 Subject: [PATCH] tendermint-rpc: Add v0.33 support (part 1) --- packages/tendermint-rpc/src/client.spec.ts | 7 - packages/tendermint-rpc/src/responses.ts | 18 +- .../tendermint-rpc/src/v0-33/hasher.spec.ts | 94 +++ packages/tendermint-rpc/src/v0-33/hasher.ts | 69 ++ packages/tendermint-rpc/src/v0-33/index.ts | 12 + packages/tendermint-rpc/src/v0-33/requests.ts | 142 ++++ .../tendermint-rpc/src/v0-33/responses.ts | 767 ++++++++++++++++++ packages/tendermint-rpc/types/responses.d.ts | 18 +- .../tendermint-rpc/types/v0-33/hasher.d.ts | 4 + .../tendermint-rpc/types/v0-33/index.d.ts | 2 + .../tendermint-rpc/types/v0-33/requests.d.ts | 18 + .../tendermint-rpc/types/v0-33/responses.d.ts | 23 + 12 files changed, 1147 insertions(+), 27 deletions(-) create mode 100644 packages/tendermint-rpc/src/v0-33/hasher.spec.ts create mode 100644 packages/tendermint-rpc/src/v0-33/hasher.ts create mode 100644 packages/tendermint-rpc/src/v0-33/index.ts create mode 100644 packages/tendermint-rpc/src/v0-33/requests.ts create mode 100644 packages/tendermint-rpc/src/v0-33/responses.ts create mode 100644 packages/tendermint-rpc/types/v0-33/hasher.d.ts create mode 100644 packages/tendermint-rpc/types/v0-33/index.d.ts create mode 100644 packages/tendermint-rpc/types/v0-33/requests.d.ts create mode 100644 packages/tendermint-rpc/types/v0-33/responses.d.ts diff --git a/packages/tendermint-rpc/src/client.spec.ts b/packages/tendermint-rpc/src/client.spec.ts index 8f1fc6e2..acccac91 100644 --- a/packages/tendermint-rpc/src/client.spec.ts +++ b/packages/tendermint-rpc/src/client.spec.ts @@ -179,7 +179,6 @@ function defaultTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor): void { // and let's query the block itself to see this transaction const block = await client.block(height); - expect(block.blockMeta.header.numTxs).toEqual(1); expect(block.block.txs.length).toEqual(1); expect(block.block.txs[0]).toEqual(tx); @@ -255,9 +254,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, appCr 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.numTxs).toEqual(0); expect(event.lastBlockId).toBeTruthy(); - expect(event.totalTxs).toBeGreaterThan(0); // merkle roots for proofs expect(event.appHash).toBeTruthy(); @@ -276,7 +273,6 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, appCr 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].totalTxs).toEqual(events[0].totalTxs); expect(events[1].appHash).toEqual(events[0].appHash); expect(events[1].consensusHash).toEqual(events[0].consensusHash); @@ -315,9 +311,7 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, appCr 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.numTxs).toEqual(1); expect(event.header.lastBlockId).toBeTruthy(); - expect(event.header.totalTxs).toBeGreaterThan(0); // merkle roots for proofs expect(event.header.appHash).toBeTruthy(); @@ -348,7 +342,6 @@ function websocketTestSuite(rpcFactory: () => RpcClient, adaptor: Adaptor, appCr 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.totalTxs).toEqual(events[0].header.totalTxs + 1); expect(events[1].header.appHash).not.toEqual(events[0].header.appHash); expect(events[1].header.validatorsHash).toEqual(events[0].header.validatorsHash); // Block body diff --git a/packages/tendermint-rpc/src/responses.ts b/packages/tendermint-rpc/src/responses.ts index 7d6e432e..8c9f4443 100644 --- a/packages/tendermint-rpc/src/responses.ts +++ b/packages/tendermint-rpc/src/responses.ts @@ -35,18 +35,17 @@ export interface AbciQueryResponse { } export interface BlockResponse { - readonly blockMeta: BlockMeta; + readonly blockId: BlockId; readonly block: Block; } export interface BlockResultsResponse { readonly height: number; readonly results: readonly TxData[]; - readonly endBlock: { - readonly validatorUpdates: readonly Validator[]; - readonly consensusUpdates?: ConsensusParams; - readonly tags?: readonly Tag[]; - }; + readonly validatorUpdates: readonly Validator[]; + readonly consensusUpdates?: ConsensusParams; + readonly beginBlock?: readonly Tag[]; + readonly endBlock?: readonly Tag[]; } export interface BlockchainResponse { @@ -214,7 +213,7 @@ export interface Evidence { export interface Commit { readonly blockId: BlockId; - readonly precommits: readonly Vote[]; + readonly signatures: readonly ValidatorSignature[]; } /** @@ -253,8 +252,6 @@ export interface Header { readonly chainId: string; readonly height: number; readonly time: ReadonlyDateWithNanoseconds; - readonly numTxs: number; - readonly totalTxs: number; // prev block info readonly lastBlockId: BlockId; @@ -327,5 +324,6 @@ export interface BlockGossipParams { } export interface EvidenceParams { - readonly maxAge: number; + readonly maxAgeNumBlocks: number; + readonly maxAgeDuration: number; } diff --git a/packages/tendermint-rpc/src/v0-33/hasher.spec.ts b/packages/tendermint-rpc/src/v0-33/hasher.spec.ts new file mode 100644 index 00000000..f78576b7 --- /dev/null +++ b/packages/tendermint-rpc/src/v0-33/hasher.spec.ts @@ -0,0 +1,94 @@ +import { fromBase64, fromHex } from "@cosmjs/encoding"; +import { ReadonlyDate } from "readonly-date"; + +import { ReadonlyDateWithNanoseconds } from "../responses"; +import { TxBytes } from "../types"; +import { hashBlock, hashTx } from "./hasher"; + +describe("Hasher", () => { + it("creates transaction hash equal to local test", () => { + // This was taken from a result from /tx_search of some random test transaction + // curl "http://localhost:11127/tx_search?query=\"tx.hash='5CB2CF94A1097A4BC19258BC2353C3E76102B6D528458BE45C855DC5563C1DB2'\"" + const txId = fromHex("5CB2CF94A1097A4BC19258BC2353C3E76102B6D528458BE45C855DC5563C1DB2"); + const txData = fromBase64("YUpxZDY2NURaUDMxPWd2TzBPdnNrVWFWYg==") as TxBytes; + expect(hashTx(txData)).toEqual(txId); + }); + + it("creates block hash equal to local test for empty block", () => { + // This was taken from a result from /block of some random empty block + // curl "http://localhost:11133/block" + const blockId = fromHex("153C484DCBC33633F0616BC019388C93DEA94F7880627976F2BFE83749E062F7"); + const time = new ReadonlyDate("2020-06-23T13:54:15.4638668Z"); + // tslint:disable-next-line:no-object-mutation + (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"); + // tslint:disable-next-line:no-object-mutation + (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/v0-33/hasher.ts b/packages/tendermint-rpc/src/v0-33/hasher.ts new file mode 100644 index 00000000..4401053b --- /dev/null +++ b/packages/tendermint-rpc/src/v0-33/hasher.ts @@ -0,0 +1,69 @@ +import { Sha256 } from "@cosmjs/crypto"; + +import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "../encodings"; +import { Header } from "../responses"; +import { BlockHash, TxBytes, TxHash } from "../types"; + +// hash is sha256 +// https://github.com/tendermint/tendermint/blob/master/UPGRADING.md#v0260 +export function hashTx(tx: TxBytes): TxHash { + const hash = new Sha256(tx).digest(); + return hash as TxHash; +} + +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): BlockHash { + 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) as BlockHash; +} diff --git a/packages/tendermint-rpc/src/v0-33/index.ts b/packages/tendermint-rpc/src/v0-33/index.ts new file mode 100644 index 00000000..bedc804c --- /dev/null +++ b/packages/tendermint-rpc/src/v0-33/index.ts @@ -0,0 +1,12 @@ +import { Adaptor } from "../adaptor"; +import { hashBlock, hashTx } from "./hasher"; +import { Params } from "./requests"; +import { Responses } from "./responses"; + +// eslint-disable-next-line @typescript-eslint/camelcase +export const v0_33: Adaptor = { + params: Params, + responses: Responses, + hashTx: hashTx, + hashBlock: hashBlock, +}; diff --git a/packages/tendermint-rpc/src/v0-33/requests.ts b/packages/tendermint-rpc/src/v0-33/requests.ts new file mode 100644 index 00000000..7526fe0a --- /dev/null +++ b/packages/tendermint-rpc/src/v0-33/requests.ts @@ -0,0 +1,142 @@ +import { toHex } from "@cosmjs/encoding"; +import { JsonRpcRequest } from "@iov/jsonrpc"; + +import { assertNotEmpty, Base64, Base64String, HexString, Integer, IntegerString, may } from "../encodings"; +import { createJsonRpcRequest } from "../jsonrpc"; +import * as requests from "../requests"; + +interface HeightParam { + readonly height?: number; +} +interface RpcHeightParam { + readonly height?: IntegerString; +} +function encodeHeightParam(param: HeightParam): RpcHeightParam { + return { + height: may(Integer.encode, param.height), + }; +} + +interface RpcBlockchainRequestParams { + readonly minHeight?: IntegerString; + readonly maxHeight?: IntegerString; +} +function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams { + return { + minHeight: may(Integer.encode, param.minHeight), + maxHeight: may(Integer.encode, param.maxHeight), + }; +} + +interface RpcAbciQueryParams { + readonly path: string; + readonly data: HexString; + readonly height?: string; + readonly prove?: boolean; +} + +function encodeAbciQueryParams(params: requests.AbciQueryParams): RpcAbciQueryParams { + return { + path: assertNotEmpty(params.path), + data: toHex(params.data) as HexString, + height: may(Integer.encode, params.height), + prove: params.prove, + }; +} + +interface RpcBroadcastTxParams { + readonly tx: Base64String; +} +function encodeBroadcastTxParams(params: requests.BroadcastTxParams): RpcBroadcastTxParams { + return { + tx: Base64.encode(assertNotEmpty(params.tx)), + }; +} + +interface RpcTxParams { + readonly hash: Base64String; + readonly prove?: boolean; +} +function encodeTxParams(params: requests.TxParams): RpcTxParams { + return { + hash: Base64.encode(assertNotEmpty(params.hash)), + prove: params.prove, + }; +} + +interface RpcTxSearchParams { + readonly query: requests.QueryString; + readonly prove?: boolean; + readonly page?: IntegerString; + readonly per_page?: IntegerString; +} +function encodeTxSearchParams(params: requests.TxSearchParams): RpcTxSearchParams { + return { + query: params.query, + prove: params.prove, + page: may(Integer.encode, params.page), + // eslint-disable-next-line @typescript-eslint/camelcase + 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, encodeHeightParam(req.params)); + } +} diff --git a/packages/tendermint-rpc/src/v0-33/responses.ts b/packages/tendermint-rpc/src/v0-33/responses.ts new file mode 100644 index 00000000..35eef209 --- /dev/null +++ b/packages/tendermint-rpc/src/v0-33/responses.ts @@ -0,0 +1,767 @@ +import { fromHex } from "@cosmjs/encoding"; +import { JsonRpcSuccessResponse } from "@iov/jsonrpc"; + +import { + assertArray, + assertBoolean, + assertNotEmpty, + assertNumber, + assertObject, + assertSet, + Base64, + Base64String, + DateTime, + DateTimeString, + dictionaryToStringMap, + Hex, + HexString, + Integer, + IntegerString, + may, + optional, +} from "../encodings"; +import * as responses from "../responses"; +import { SubscriptionEvent } from "../rpcclients"; +import { IpPortString, TxBytes, TxHash, ValidatorPubkey, ValidatorSignature } from "../types"; +import { hashTx } from "./hasher"; + +interface AbciInfoResult { + readonly response: RpcAbciInfoResponse; +} + +interface RpcAbciInfoResponse { + readonly data?: string; + readonly last_block_height?: IntegerString; + readonly last_block_app_hash?: Base64String; +} + +function decodeAbciInfo(data: RpcAbciInfoResponse): responses.AbciInfoResponse { + return { + data: data.data, + lastBlockHeight: may(Integer.parse, data.last_block_height), + lastBlockAppHash: may(Base64.decode, data.last_block_app_hash), + }; +} + +interface AbciQueryResult { + readonly response: RpcAbciQueryResponse; +} + +interface RpcAbciQueryResponse { + readonly key: Base64String; + readonly value?: Base64String; + readonly proof?: Base64String; + readonly height?: IntegerString; + readonly index?: IntegerString; + readonly code?: IntegerString; // only for errors + readonly log?: string; +} + +function decodeAbciQuery(data: RpcAbciQueryResponse): responses.AbciQueryResponse { + return { + key: Base64.decode(optional(data.key, "" as Base64String)), + value: Base64.decode(optional(data.value, "" as Base64String)), + // proof: may(Base64.decode, data.proof), + height: may(Integer.parse, data.height), + code: may(Integer.parse, data.code), + index: may(Integer.parse, data.index), + log: data.log, + }; +} + +interface RpcTag { + readonly key: Base64String; + readonly value: Base64String; +} + +function decodeTag(tag: RpcTag): responses.Tag { + return { + key: Base64.decode(assertNotEmpty(tag.key)), + value: Base64.decode(assertNotEmpty(tag.value)), + }; +} + +function decodeTags(tags: readonly RpcTag[]): readonly responses.Tag[] { + return assertArray(tags).map(decodeTag); +} + +interface RpcEvent { + readonly type: string; + readonly attributes: readonly RpcTag[]; +} + +function decodeEvent(event: RpcEvent): responses.Event { + return { + type: event.type, + attributes: decodeTags(event.attributes), + }; +} + +function decodeEvents(events: readonly RpcEvent[]): readonly responses.Event[] { + return assertArray(events).map(decodeEvent); +} + +interface RpcTxData { + readonly code?: number; + readonly log?: string; + readonly data?: Base64String; + readonly events?: readonly RpcEvent[]; +} + +function decodeTxData(data: RpcTxData): responses.TxData { + return { + data: may(Base64.decode, data.data), + log: data.log, + code: Integer.parse(assertNumber(optional(data.code, 0))), + events: may(decodeEvents, data.events), + }; +} + +// yes, a different format for status and dump consensus state +interface RpcPubkey { + readonly type: string; + readonly value: Base64String; +} + +function decodePubkey(data: RpcPubkey): ValidatorPubkey { + if (data.type === "tendermint/PubKeyEd25519") { + // go-amino special code + return { + algorithm: "ed25519", + data: Base64.decode(assertNotEmpty(data.value)), + }; + } + throw new Error(`unknown pubkey type: ${data.type}`); +} + +// for evidence, block results, etc. +interface RpcValidatorUpdate { + readonly address: HexString; + readonly pub_key: RpcPubkey; + readonly voting_power: IntegerString; +} + +function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.Validator { + return { + pubkey: decodePubkey(assertObject(data.pub_key)), + votingPower: Integer.parse(assertNotEmpty(data.voting_power)), + address: Hex.decode(assertNotEmpty(data.address)), + }; +} + +interface RpcBlockParams { + readonly max_bytes: IntegerString; + readonly max_gas: IntegerString; +} + +/** + * 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: IntegerString; + readonly max_age_duration: IntegerString; +} + +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: IntegerString; + readonly txs_results: readonly RpcTxData[] | null; + readonly begin_block_events: null; + readonly end_block_events: null; + readonly validator_updates: null; + readonly consensus_param_updates: null; +} + +function decodeBlockResults(data: RpcBlockResultsResponse): responses.BlockResultsResponse { + const results = optional(data.txs_results, [] as readonly RpcTxData[]); + const validatorUpdates = optional(data.validator_updates, [] as readonly RpcValidatorUpdate[]); + return { + height: Integer.parse(assertNotEmpty(data.height)), + results: results.map(decodeTxData), + validatorUpdates: validatorUpdates.map(decodeValidatorUpdate), + consensusUpdates: may(decodeConsensusParams, data.consensus_param_updates), + beginBlock: may(decodeTags, data.begin_block_events), + endBlock: may(decodeTags, data.end_block_events), + }; +} + +interface RpcBlockId { + readonly hash: HexString; + readonly parts: { + readonly total: IntegerString; + readonly hash: HexString; + }; +} + +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: IntegerString; + readonly app: IntegerString; +} + +function decodeBlockVersion(data: RpcBlockVersion): responses.Version { + return { + block: Integer.parse(data.block), + app: Integer.parse(data.app), + }; +} + +interface RpcHeader { + readonly version: RpcBlockVersion; + readonly chain_id: string; + readonly height: IntegerString; + readonly time: DateTimeString; + readonly num_txs: IntegerString; + readonly total_txs: IntegerString; + + readonly last_block_id: RpcBlockId; + + readonly last_commit_hash: HexString; + readonly data_hash: HexString; + + readonly validators_hash: HexString; + readonly next_validators_hash: HexString; + readonly consensus_hash: HexString; + readonly app_hash: HexString; + readonly last_results_hash: HexString; + + readonly evidence_hash: HexString; + readonly proposer_address: HexString; +} + +function decodeHeader(data: RpcHeader): responses.Header { + return { + version: decodeBlockVersion(data.version), + chainId: assertNotEmpty(data.chain_id), + height: Integer.parse(assertNotEmpty(data.height)), + time: DateTime.decode(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: IntegerString; + 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 { + readonly hash: HexString; +} + +function decodeBroadcastTxSync(data: RpcBroadcastTxSyncResponse): responses.BroadcastTxSyncResponse { + return { + ...decodeTxData(data), + hash: fromHex(assertNotEmpty(data.hash)) as TxHash, + }; +} + +interface RpcBroadcastTxCommitResponse { + readonly height?: IntegerString; + readonly hash: HexString; + readonly check_tx: RpcTxData; + readonly deliver_tx?: RpcTxData; +} + +function decodeBroadcastTxCommit(data: RpcBroadcastTxCommitResponse): responses.BroadcastTxCommitResponse { + return { + height: may(Integer.parse, data.height), + hash: fromHex(assertNotEmpty(data.hash)) as TxHash, + checkTx: decodeTxData(assertObject(data.check_tx)), + deliverTx: may(decodeTxData, data.deliver_tx), + }; +} + +type RpcSignature = { + readonly block_id_flag: number; + readonly validator_address: HexString; + readonly timestamp: DateTimeString; + readonly signature: Base64String; +}; + +function decodeSignature(data: RpcSignature): ValidatorSignature { + return { + algorithm: "ed25519", + data: Base64.decode(assertNotEmpty(data.signature)), + }; +} + +interface RpcCommit { + readonly block_id: RpcBlockId; + readonly signatures: readonly RpcSignature[]; +} + +function decodeCommit(data: RpcCommit): responses.Commit { + return { + blockId: decodeBlockId(assertObject(data.block_id)), + signatures: assertArray(data.signatures).map(decodeSignature), + }; +} + +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 { + readonly pub_key: RpcPubkey; + readonly power: IntegerString; + readonly name?: string; +} + +function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Validator { + return { + pubkey: decodePubkey(assertObject(data.pub_key)), + votingPower: Integer.parse(assertNotEmpty(data.power)), + name: data.name, + }; +} + +interface RpcGenesisResponse { + readonly genesis_time: DateTimeString; + readonly chain_id: string; + readonly consensus_params: RpcConsensusParams; + readonly validators: readonly RpcValidatorGenesis[]; + readonly app_hash: HexString; + readonly app_state: {} | undefined; +} + +interface GenesisResult { + readonly genesis: RpcGenesisResponse; +} + +function decodeGenesis(data: RpcGenesisResponse): responses.GenesisResponse { + return { + genesisTime: DateTime.decode(assertNotEmpty(data.genesis_time)), + chainId: assertNotEmpty(data.chain_id), + consensusParams: decodeConsensusParams(data.consensus_params), + 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 { + readonly address: HexString; + readonly pub_key: RpcPubkey; + readonly voting_power: IntegerString; +} + +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 { + readonly id: HexString; + readonly listen_addr: IpPortString; + readonly network: string; + readonly version: string; + readonly channels: string; // ??? + readonly moniker: string; + readonly protocol_version: { + readonly p2p: IntegerString; + readonly block: IntegerString; + readonly app: IntegerString; + }; + /** + * Additional information. E.g. + * { + * "tx_index": "on", + * "rpc_address":"tcp://0.0.0.0:26657" + * } + */ + readonly other: object; +} + +function decodeNodeInfo(data: RpcNodeInfo): responses.NodeInfo { + return { + id: fromHex(assertNotEmpty(data.id)), + listenAddr: assertNotEmpty(data.listen_addr), + network: assertNotEmpty(data.network), + version: assertNotEmpty(data.version), + 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 { + readonly latest_block_hash: HexString; + readonly latest_app_hash: HexString; + readonly latest_block_height: IntegerString; + readonly latest_block_time: DateTimeString; + 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: DateTime.decode(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: + * { + * "RootHash": "10A1A17D5F818099B5CAB5B91733A3CC27C0DB6CE2D571AC27FB970C314308BB", + * "Data": "ZVlERVhDV2lVNEUwPXhTUjc4Tmp2QkNVSg==", + * "Proof": { + * "total": "1", + * "index": "0", + * "leaf_hash": "EKGhfV+BgJm1yrW5FzOjzCfA22zi1XGsJ/uXDDFDCLs=", + * "aunts": [] + * } + * } + */ +interface RpcTxProof { + readonly Data: Base64String; + readonly RootHash: HexString; + readonly Proof: { + readonly total: IntegerString; + readonly index: IntegerString; + readonly leaf_hash: Base64String; + readonly aunts: readonly Base64String[]; + }; +} + +function decodeTxProof(data: RpcTxProof): responses.TxProof { + return { + data: Base64.decode(assertNotEmpty(data.Data)), + rootHash: fromHex(assertNotEmpty(data.RootHash)), + proof: { + total: Integer.parse(assertNotEmpty(data.Proof.total)), + index: Integer.parse(assertNotEmpty(data.Proof.index)), + leafHash: Base64.decode(assertNotEmpty(data.Proof.leaf_hash)), + aunts: assertArray(data.Proof.aunts).map(Base64.decode), + }, + }; +} + +interface RpcTxResponse { + readonly tx: Base64String; + readonly tx_result: RpcTxData; + readonly height: IntegerString; + readonly index: number; + readonly hash: HexString; + readonly proof?: RpcTxProof; +} + +function decodeTxResponse(data: RpcTxResponse): responses.TxResponse { + return { + tx: Base64.decode(assertNotEmpty(data.tx)) as TxBytes, + result: decodeTxData(assertObject(data.tx_result)), + height: Integer.parse(assertNotEmpty(data.height)), + index: Integer.parse(assertNumber(data.index)), + hash: fromHex(assertNotEmpty(data.hash)) as TxHash, + proof: may(decodeTxProof, data.proof), + }; +} + +interface RpcTxSearchResponse { + readonly txs: readonly RpcTxResponse[]; + readonly total_count: IntegerString; +} + +function decodeTxSearch(data: RpcTxSearchResponse): responses.TxSearchResponse { + return { + totalCount: Integer.parse(assertNotEmpty(data.total_count)), + txs: assertArray(data.txs).map(decodeTxResponse), + }; +} + +interface RpcTxEvent { + readonly tx: Base64String; + readonly result: RpcTxData; + readonly height: IntegerString; + readonly index: number; +} + +function decodeTxEvent(data: RpcTxEvent): responses.TxEvent { + const tx = Base64.decode(assertNotEmpty(data.tx)) as TxBytes; + return { + tx: tx, + hash: hashTx(tx), + result: decodeTxData(data.result), + height: Integer.parse(assertNotEmpty(data.height)), + index: Integer.parse(assertNumber(data.index)), + }; +} + +// for validators +interface RpcValidatorData extends RpcValidatorUpdate { + readonly accum?: IntegerString; +} + +function decodeValidatorData(data: RpcValidatorData): responses.Validator { + return { + ...decodeValidatorUpdate(data), + accum: may(Integer.parse, data.accum), + }; +} + +interface RpcValidatorsResponse { + readonly block_height: IntegerString; + readonly validators: readonly RpcValidatorData[]; +} + +function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse { + return { + blockHeight: Integer.parse(assertNotEmpty(data.block_height)), + results: assertArray(data.validators).map(decodeValidatorData), + }; +} + +interface RpcEvidence { + readonly type: string; + readonly validator: RpcValidatorUpdate; + readonly height: IntegerString; + readonly time: IntegerString; + readonly totalVotingPower: IntegerString; +} + +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: { + readonly txs?: readonly Base64String[]; + }; + 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(Base64.decode) : [], + 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 this.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/types/responses.d.ts b/packages/tendermint-rpc/types/responses.d.ts index cf409ec4..6565e6ba 100644 --- a/packages/tendermint-rpc/types/responses.d.ts +++ b/packages/tendermint-rpc/types/responses.d.ts @@ -30,17 +30,16 @@ export interface AbciQueryResponse { readonly log?: string; } export interface BlockResponse { - readonly blockMeta: BlockMeta; + readonly blockId: BlockId; readonly block: Block; } export interface BlockResultsResponse { readonly height: number; readonly results: readonly TxData[]; - readonly endBlock: { - readonly validatorUpdates: readonly Validator[]; - readonly consensusUpdates?: ConsensusParams; - readonly tags?: readonly Tag[]; - }; + readonly validatorUpdates: readonly Validator[]; + readonly consensusUpdates?: ConsensusParams; + readonly beginBlock?: readonly Tag[]; + readonly endBlock?: readonly Tag[]; } export interface BlockchainResponse { readonly lastHeight: number; @@ -170,7 +169,7 @@ export interface Evidence { } export interface Commit { readonly blockId: BlockId; - readonly precommits: readonly Vote[]; + readonly signatures: readonly ValidatorSignature[]; } /** * raw values from https://github.com/tendermint/tendermint/blob/dfa9a9a30a666132425b29454e90a472aa579a48/types/vote.go#L44 @@ -201,8 +200,6 @@ export interface Header { readonly chainId: string; readonly height: number; readonly time: ReadonlyDateWithNanoseconds; - readonly numTxs: number; - readonly totalTxs: number; readonly lastBlockId: BlockId; readonly lastCommitHash: Uint8Array; readonly dataHash: Uint8Array; @@ -258,5 +255,6 @@ export interface BlockGossipParams { readonly blockPartSizeBytes: number; } export interface EvidenceParams { - readonly maxAge: number; + readonly maxAgeNumBlocks: number; + readonly maxAgeDuration: number; } diff --git a/packages/tendermint-rpc/types/v0-33/hasher.d.ts b/packages/tendermint-rpc/types/v0-33/hasher.d.ts new file mode 100644 index 00000000..de2e7e90 --- /dev/null +++ b/packages/tendermint-rpc/types/v0-33/hasher.d.ts @@ -0,0 +1,4 @@ +import { Header } from "../responses"; +import { BlockHash, TxBytes, TxHash } from "../types"; +export declare function hashTx(tx: TxBytes): TxHash; +export declare function hashBlock(header: Header): BlockHash; diff --git a/packages/tendermint-rpc/types/v0-33/index.d.ts b/packages/tendermint-rpc/types/v0-33/index.d.ts new file mode 100644 index 00000000..f5b2b014 --- /dev/null +++ b/packages/tendermint-rpc/types/v0-33/index.d.ts @@ -0,0 +1,2 @@ +import { Adaptor } from "../adaptor"; +export declare const v0_33: Adaptor; diff --git a/packages/tendermint-rpc/types/v0-33/requests.d.ts b/packages/tendermint-rpc/types/v0-33/requests.d.ts new file mode 100644 index 00000000..af972c4a --- /dev/null +++ b/packages/tendermint-rpc/types/v0-33/requests.d.ts @@ -0,0 +1,18 @@ +import { JsonRpcRequest } from "@iov/jsonrpc"; +import * as requests from "../requests"; +export declare class Params { + static encodeAbciInfo(req: requests.AbciInfoRequest): JsonRpcRequest; + static encodeAbciQuery(req: requests.AbciQueryRequest): JsonRpcRequest; + static encodeBlock(req: requests.BlockRequest): JsonRpcRequest; + static encodeBlockchain(req: requests.BlockchainRequest): JsonRpcRequest; + static encodeBlockResults(req: requests.BlockResultsRequest): JsonRpcRequest; + static encodeBroadcastTx(req: requests.BroadcastTxRequest): JsonRpcRequest; + static encodeCommit(req: requests.CommitRequest): JsonRpcRequest; + static encodeGenesis(req: requests.GenesisRequest): JsonRpcRequest; + static encodeHealth(req: requests.HealthRequest): JsonRpcRequest; + static encodeStatus(req: requests.StatusRequest): JsonRpcRequest; + static encodeSubscribe(req: requests.SubscribeRequest): JsonRpcRequest; + static encodeTx(req: requests.TxRequest): JsonRpcRequest; + static encodeTxSearch(req: requests.TxSearchRequest): JsonRpcRequest; + static encodeValidators(req: requests.ValidatorsRequest): JsonRpcRequest; +} diff --git a/packages/tendermint-rpc/types/v0-33/responses.d.ts b/packages/tendermint-rpc/types/v0-33/responses.d.ts new file mode 100644 index 00000000..05fad338 --- /dev/null +++ b/packages/tendermint-rpc/types/v0-33/responses.d.ts @@ -0,0 +1,23 @@ +import { JsonRpcSuccessResponse } from "@iov/jsonrpc"; +import * as responses from "../responses"; +import { SubscriptionEvent } from "../rpcclients"; +export declare class Responses { + static decodeAbciInfo(response: JsonRpcSuccessResponse): responses.AbciInfoResponse; + static decodeAbciQuery(response: JsonRpcSuccessResponse): responses.AbciQueryResponse; + static decodeBlock(response: JsonRpcSuccessResponse): responses.BlockResponse; + static decodeBlockResults(response: JsonRpcSuccessResponse): responses.BlockResultsResponse; + static decodeBlockchain(response: JsonRpcSuccessResponse): responses.BlockchainResponse; + static decodeBroadcastTxSync(response: JsonRpcSuccessResponse): responses.BroadcastTxSyncResponse; + static decodeBroadcastTxAsync(response: JsonRpcSuccessResponse): responses.BroadcastTxAsyncResponse; + static decodeBroadcastTxCommit(response: JsonRpcSuccessResponse): responses.BroadcastTxCommitResponse; + static decodeCommit(response: JsonRpcSuccessResponse): responses.CommitResponse; + static decodeGenesis(response: JsonRpcSuccessResponse): responses.GenesisResponse; + static decodeHealth(): responses.HealthResponse; + static decodeStatus(response: JsonRpcSuccessResponse): responses.StatusResponse; + static decodeNewBlockEvent(event: SubscriptionEvent): responses.NewBlockEvent; + static decodeNewBlockHeaderEvent(event: SubscriptionEvent): responses.NewBlockHeaderEvent; + static decodeTxEvent(event: SubscriptionEvent): responses.TxEvent; + static decodeTx(response: JsonRpcSuccessResponse): responses.TxResponse; + static decodeTxSearch(response: JsonRpcSuccessResponse): responses.TxSearchResponse; + static decodeValidators(response: JsonRpcSuccessResponse): responses.ValidatorsResponse; +}