tendermint-rpc: Remove v0.31 support

This commit is contained in:
willclarktech 2020-06-15 16:47:55 +01:00
parent 65dbe10b68
commit caf5ff7ce8
No known key found for this signature in database
GPG Key ID: 551A86E2E398ADF7
14 changed files with 5 additions and 1158 deletions

View File

@ -16,9 +16,7 @@ automatically, and call:
```ts
import { Client } from "@iov/tendermint-rpc";
const client = await Client.connect(
"ws://rpc-private-a-x-exchangenet.iov.one:16657",
);
const client = await Client.connect("ws://rpc-private-a-x-exchangenet.iov.one:16657");
const genesis = await client.genesis();
const status = await client.status();
@ -69,7 +67,7 @@ and another for decoding
[Responses](https://iov-one.github.io/iov-core-docs/latest/iov-tendermint-rpc/classes/responses.html).
The Tendermint version-specific functionality is implemented in global objects
(like e.g.
[v0_31](https://iov-one.github.io/iov-core-docs/latest/iov-tendermint-rpc/globals.html#v0_31)).
[v0_32](https://iov-one.github.io/iov-core-docs/latest/iov-tendermint-rpc/globals.html#v0_32)).
This knowledge is mainly for those who want to add support for new versions,
which should be added to the
[auto-detect method](https://iov-one.github.io/iov-core-docs/latest/iov-tendermint-rpc/classes/client.html#detectversion).

View File

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/camelcase */
// This module exposes translators for multiple tendermint versions
// Pick a version that matches the server to properly encode the data types
import { Adaptor } from "./adaptor";
import { v0_31 } from "./v0-31";
import { v0_32 } from "./v0-32";
/**
@ -11,9 +11,7 @@ import { v0_32 } from "./v0-32";
* @param version full Tendermint version string, e.g. "0.20.1"
*/
export function adaptorForVersion(version: string): Adaptor {
if (version.startsWith("0.31.")) {
return v0_31;
} else if (version.startsWith("0.32.")) {
if (version.startsWith("0.32.")) {
return v0_32;
} else {
throw new Error(`Unsupported tendermint version: ${version}`);

View File

@ -17,11 +17,6 @@ export interface TendermintInstance {
* docker container kill <container id from 1st column>
*/
export const tendermintInstances: readonly TendermintInstance[] = [
{
url: "localhost:11131",
version: "0.31.x",
appCreator: "Cosmoshi Netowoko",
},
{
url: "localhost:11132",
version: "0.32.x",

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/camelcase */
// exported to access version-specific hashing
export { v0_31 } from "./v0-31";
export { v0_32 } from "./v0-32";
export { Client } from "./client";

View File

@ -1,98 +0,0 @@
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:11131/block"
const blockId = fromHex("5B5D3F7E77A4BD6CB6067947E478BC3BD493DD24A981535F0ADEBDAAA0498480");
const time = new ReadonlyDate("2019-09-19T10:41:24.898178746Z");
// tslint:disable-next-line:no-object-mutation
(time as any).nanoseconds = 178746;
const blockData = {
version: {
block: 10,
app: 1,
},
chainId: "test-chain-RRlV24",
height: 2195,
time: time as ReadonlyDateWithNanoseconds,
numTxs: 0,
totalTxs: 20,
lastBlockId: {
hash: fromHex("1D38C4FE5C1D8C3CC1F47602BF107C9B269BA7DA3514DEDF958F5A33AB75C06B"),
parts: {
total: 1,
hash: fromHex("C441341B7D846DDA6AF72F83DF68C9AF93665FE5280B136CA29C7411D280DAEC"),
},
},
lastCommitHash: fromHex("0C5EEF7AE1275337BFAA173F57799AA90830E74AFF3FB03D1F579DA37BCAEAB1"),
dataHash: fromHex(""),
validatorsHash: fromHex("44D7D0BE3C70B58DA87696102E3A52E5C9FA98A717E56D02987DA8CAE86F03F4"),
nextValidatorsHash: fromHex("44D7D0BE3C70B58DA87696102E3A52E5C9FA98A717E56D02987DA8CAE86F03F4"),
consensusHash: fromHex("048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F"),
appHash: fromHex("2800000000000000"),
lastResultsHash: fromHex(""),
evidenceHash: fromHex(""),
proposerAddress: fromHex("057B8C349E591579EDFCC0E5D5402E3076E99675"),
};
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:11131/block?height=5940"
const blockId = fromHex("1C4777AFBBA49E15D031A830E62E7BE986823938732B872C02B8A3D16BD3163B");
const time = new ReadonlyDate("2019-09-24T10:51:28.240847497Z");
// tslint:disable-next-line:no-object-mutation
(time as any).nanoseconds = 847497;
const blockData = {
version: {
block: 10,
app: 1,
},
chainId: "test-chain-lY9FO6",
height: 5940,
time: time as ReadonlyDateWithNanoseconds,
numTxs: 1,
totalTxs: 61,
lastBlockId: {
hash: fromHex("D2983E6AEEFC55E0A46565CD2274CCD21CB013F5602B0C35A423A99D1120DB13"),
parts: {
total: 1,
hash: fromHex("AA55D7F92AD3A9CFDA8C5E45F95B03AEF9FB38AB984FD762E5CE20791324369D"),
},
},
lastCommitHash: fromHex("5DBFFDBE41878AEB947176D3E0B0DC70850B0A61F8B709ED132FEA59664DFCE5"),
dataHash: fromHex("90FE1A62418F68B411915EEF6792B134693D9D0148432BA661D91213B0CCD15A"),
validatorsHash: fromHex("0A4647900ED90CC605E851BBB4946D7B9D1830F293BC87F3CE16AEFF4E4C77E2"),
nextValidatorsHash: fromHex("0A4647900ED90CC605E851BBB4946D7B9D1830F293BC87F3CE16AEFF4E4C77E2"),
consensusHash: fromHex("048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F"),
appHash: fromHex("7800000000000000"),
lastResultsHash: fromHex("6E340B9CFFB37A989CA544E6BB780A2C78901D3FB33738768511A30617AFA01D"),
evidenceHash: fromHex(""),
proposerAddress: fromHex("6BCBB90987613FE15D3DEFA4920E9F98425698FF"),
};
expect(hashBlock(blockData)).toEqual(blockId);
});
});

View File

@ -1,71 +0,0 @@
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),
encodeInt(header.numTxs),
encodeInt(header.totalTxs),
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;
}

View File

@ -1,12 +0,0 @@
import { Adaptor } from "../adaptor";
import { hashBlock, hashTx } from "./hasher";
import { Params } from "./requests";
import { Responses } from "./responses";
// tslint:disable-next-line:variable-name
export const v0_31: Adaptor = {
params: Params,
responses: Responses,
hashTx: hashTx,
hashBlock: hashBlock,
};

View File

@ -1,142 +0,0 @@
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));
}
}

View File

@ -1,773 +0,0 @@
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 RpcTxData {
readonly code?: number;
readonly log?: string;
readonly data?: Base64String;
readonly tags?: readonly RpcTag[];
}
function decodeTxData(data: RpcTxData): responses.TxData {
return {
data: may(Base64.decode, data.data),
log: data.log,
code: Integer.parse(assertNumber(optional<number>(data.code, 0))),
tags: may(decodeTags, data.tags),
};
}
// 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: IntegerString;
}
function decodeEvidenceParams(data: RpcEvidenceParams): responses.EvidenceParams {
return {
maxAge: Integer.parse(assertNotEmpty(data.max_age)),
};
}
/**
* Example data:
* {
* "block": {
* "max_bytes": "22020096",
* "max_gas": "-1",
* "time_iota_ms": "1000"
* },
* "evidence": {
* "max_age": "100000"
* },
* "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 results: {
readonly DeliverTx: readonly RpcTxData[];
readonly EndBlock: {
readonly validator_updates?: readonly RpcValidatorUpdate[];
readonly consensus_param_updates?: RpcConsensusParams;
readonly tags?: readonly RpcTag[];
};
};
}
function decodeBlockResults(data: RpcBlockResultsResponse): responses.BlockResultsResponse {
const res = optional(data.results.DeliverTx, [] as readonly RpcTxData[]);
const end = data.results.EndBlock;
const validators = optional(end.validator_updates, [] as readonly RpcValidatorUpdate[]);
return {
height: Integer.parse(assertNotEmpty(data.height)),
results: assertArray(res).map(decodeTxData),
endBlock: {
validatorUpdates: assertArray(validators).map(decodeValidatorUpdate),
consensusUpdates: may(decodeConsensusParams, end.consensus_param_updates),
tags: may(decodeTags, end.tags),
},
};
}
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)),
numTxs: Integer.parse(assertNotEmpty(data.num_txs)),
totalTxs: Integer.parse(assertNotEmpty(data.total_txs)),
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 = Base64String;
function decodeSignature(data: RpcSignature): ValidatorSignature {
return {
algorithm: "ed25519",
data: Base64.decode(assertNotEmpty(data)),
};
}
interface RpcVote {
readonly type: number;
readonly validator_address: HexString;
readonly validator_index: IntegerString;
readonly height: IntegerString;
readonly round: IntegerString;
readonly timestamp: DateTimeString;
readonly block_id: RpcBlockId;
readonly signature: RpcSignature;
}
function decodeVote(data: RpcVote): responses.Vote {
return {
type: Integer.parse(assertNumber(data.type)),
validatorAddress: fromHex(assertNotEmpty(data.validator_address)),
validatorIndex: Integer.parse(assertNotEmpty(data.validator_index)),
height: Integer.parse(assertNotEmpty(data.height)),
round: Integer.parse(assertNotEmpty(data.round)),
timestamp: DateTime.decode(assertNotEmpty(data.timestamp)),
blockId: decodeBlockId(assertObject(data.block_id)),
signature: decodeSignature(assertNotEmpty(data.signature)),
};
}
interface RpcCommit {
readonly block_id: RpcBlockId;
readonly precommits: readonly RpcVote[];
}
function decodeCommit(data: RpcCommit): responses.Commit {
return {
blockId: decodeBlockId(assertObject(data.block_id)),
precommits: assertArray(data.precommits).map(decodeVote),
};
}
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_meta: RpcBlockMeta;
readonly block: RpcBlock;
}
function decodeBlockResponse(data: RpcBlockResponse): responses.BlockResponse {
return {
blockMeta: decodeBlockMeta(data.block_meta),
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);
}
}

View File

@ -1,4 +1,3 @@
export { v0_31 } from "./v0-31";
export { v0_32 } from "./v0-32";
export { Client } from "./client";
export {

View File

@ -1,4 +0,0 @@
import { Header } from "../responses";
import { BlockHash, TxBytes, TxHash } from "../types";
export declare function hashTx(tx: TxBytes): TxHash;
export declare function hashBlock(header: Header): BlockHash;

View File

@ -1,2 +0,0 @@
import { Adaptor } from "../adaptor";
export declare const v0_31: Adaptor;

View File

@ -1,18 +0,0 @@
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;
}

View File

@ -1,23 +0,0 @@
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;
}