tendermint-rpc: Add v0.33 support (part 1)
This commit is contained in:
parent
6f07e90587
commit
9ba93ebc9e
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
94
packages/tendermint-rpc/src/v0-33/hasher.spec.ts
Normal file
94
packages/tendermint-rpc/src/v0-33/hasher.spec.ts
Normal file
@ -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);
|
||||
});
|
||||
});
|
||||
69
packages/tendermint-rpc/src/v0-33/hasher.ts
Normal file
69
packages/tendermint-rpc/src/v0-33/hasher.ts
Normal file
@ -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;
|
||||
}
|
||||
12
packages/tendermint-rpc/src/v0-33/index.ts
Normal file
12
packages/tendermint-rpc/src/v0-33/index.ts
Normal file
@ -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,
|
||||
};
|
||||
142
packages/tendermint-rpc/src/v0-33/requests.ts
Normal file
142
packages/tendermint-rpc/src/v0-33/requests.ts
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
767
packages/tendermint-rpc/src/v0-33/responses.ts
Normal file
767
packages/tendermint-rpc/src/v0-33/responses.ts
Normal file
@ -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<number>(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);
|
||||
}
|
||||
}
|
||||
18
packages/tendermint-rpc/types/responses.d.ts
vendored
18
packages/tendermint-rpc/types/responses.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
||||
4
packages/tendermint-rpc/types/v0-33/hasher.d.ts
vendored
Normal file
4
packages/tendermint-rpc/types/v0-33/hasher.d.ts
vendored
Normal file
@ -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;
|
||||
2
packages/tendermint-rpc/types/v0-33/index.d.ts
vendored
Normal file
2
packages/tendermint-rpc/types/v0-33/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import { Adaptor } from "../adaptor";
|
||||
export declare const v0_33: Adaptor;
|
||||
18
packages/tendermint-rpc/types/v0-33/requests.d.ts
vendored
Normal file
18
packages/tendermint-rpc/types/v0-33/requests.d.ts
vendored
Normal file
@ -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;
|
||||
}
|
||||
23
packages/tendermint-rpc/types/v0-33/responses.d.ts
vendored
Normal file
23
packages/tendermint-rpc/types/v0-33/responses.d.ts
vendored
Normal file
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user