Merge pull request #1187 from cosmos/large-voting-powers
Support voting powers > MAX_SAFE_INTEGER
This commit is contained in:
commit
47bcd344d3
@ -40,9 +40,13 @@ and this project adheres to
|
||||
- all: Upgrade cosmjs-types to 0.5 ([#1131]).
|
||||
- @cosmjs/stargate: Change `packetCommitment` parameter `sequence` type from
|
||||
`Long` to `number` ([#1168]).
|
||||
- @cosmjs/tendermint-rpc: The type of `votingPower` fields was changed from
|
||||
`number` to `bigint` as those values can exceed the safe integer range
|
||||
([#1133]).
|
||||
|
||||
[#1131]: https://github.com/cosmos/cosmjs/pull/1131
|
||||
[#1168]: https://github.com/cosmos/cosmjs/pull/1168
|
||||
[#1133]: https://github.com/cosmos/cosmjs/issues/1133
|
||||
|
||||
## [0.28.9] - 2022-06-21
|
||||
|
||||
|
||||
40
packages/tendermint-rpc/src/inthelpers.spec.ts
Normal file
40
packages/tendermint-rpc/src/inthelpers.spec.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { apiToBigInt } from "./inthelpers";
|
||||
|
||||
describe("inthelpers", () => {
|
||||
describe("apiToBigInt", () => {
|
||||
it("works for positive an negative ints", () => {
|
||||
expect(apiToBigInt("0")).toEqual(BigInt(0));
|
||||
expect(apiToBigInt("1")).toEqual(BigInt(1));
|
||||
expect(apiToBigInt("100")).toEqual(BigInt(100));
|
||||
expect(apiToBigInt("-1")).toEqual(BigInt(-1));
|
||||
expect(apiToBigInt("-100")).toEqual(BigInt(-100));
|
||||
expect(apiToBigInt("9007199254740991")).toEqual(BigInt(9007199254740991));
|
||||
expect(apiToBigInt("-9007199254740991")).toEqual(BigInt(-9007199254740991));
|
||||
// uint64 max
|
||||
expect(apiToBigInt("18446744073709551615")).toEqual(BigInt("18446744073709551615"));
|
||||
// int64 min/max
|
||||
expect(apiToBigInt("-9223372036854775808")).toEqual(BigInt("-9223372036854775808"));
|
||||
expect(apiToBigInt("9223372036854775807")).toEqual(BigInt("9223372036854775807"));
|
||||
});
|
||||
|
||||
it("throws for ill-formatted inputs", () => {
|
||||
// empty
|
||||
expect(() => apiToBigInt("")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("-")).toThrowError(/invalid string format/i);
|
||||
|
||||
// non decimal representation
|
||||
expect(() => apiToBigInt("0x0")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("0x01")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("0x")).toThrowError(/invalid string format/i);
|
||||
|
||||
// decimal points
|
||||
expect(() => apiToBigInt("1.0")).toThrowError(/invalid string format/i);
|
||||
|
||||
// Invalid dashes
|
||||
expect(() => apiToBigInt("1-")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("--1")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("1-1")).toThrowError(/invalid string format/i);
|
||||
expect(() => apiToBigInt("-1-1")).toThrowError(/invalid string format/i);
|
||||
});
|
||||
});
|
||||
});
|
||||
36
packages/tendermint-rpc/src/inthelpers.ts
Normal file
36
packages/tendermint-rpc/src/inthelpers.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Int53 } from "@cosmjs/math";
|
||||
|
||||
import { assertString } from "./tendermint34/encodings";
|
||||
|
||||
/**
|
||||
* Takes an integer value from the Tendermint RPC API and
|
||||
* returns it as number.
|
||||
*
|
||||
* Only works within the safe integer range.
|
||||
*/
|
||||
export function apiToSmallInt(input: string | number): number {
|
||||
const asInt = typeof input === "number" ? new Int53(input) : Int53.fromString(input);
|
||||
return asInt.toNumber();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an integer value from the Tendermint RPC API and
|
||||
* returns it as BigInt.
|
||||
*
|
||||
* This supports the full uint64 and int64 ranges.
|
||||
*/
|
||||
export function apiToBigInt(input: string): bigint {
|
||||
assertString(input); // Runtime check on top of TypeScript just to be safe for semi-trusted API types
|
||||
if (!input.match(/^-?[0-9]+$/)) {
|
||||
throw new Error("Invalid string format");
|
||||
}
|
||||
return BigInt(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an integer in the safe integer range and returns
|
||||
* a string representation to be used in the Tendermint RPC API.
|
||||
*/
|
||||
export function smallIntToApi(num: number): string {
|
||||
return new Int53(num).toString();
|
||||
}
|
||||
@ -2,8 +2,9 @@
|
||||
import { toBase64, toHex } from "@cosmjs/encoding";
|
||||
import { JsonRpcRequest } from "@cosmjs/json-rpc";
|
||||
|
||||
import { smallIntToApi } from "../../inthelpers";
|
||||
import { createJsonRpcRequest } from "../../jsonrpc";
|
||||
import { assertNotEmpty, Integer, may } from "../encodings";
|
||||
import { assertNotEmpty, may } from "../encodings";
|
||||
import * as requests from "../requests";
|
||||
|
||||
interface HeightParam {
|
||||
@ -14,7 +15,7 @@ interface RpcHeightParam {
|
||||
}
|
||||
function encodeHeightParam(param: HeightParam): RpcHeightParam {
|
||||
return {
|
||||
height: may(Integer.encode, param.height),
|
||||
height: may(smallIntToApi, param.height),
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,8 +26,8 @@ interface RpcBlockchainRequestParams {
|
||||
|
||||
function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams {
|
||||
return {
|
||||
minHeight: may(Integer.encode, param.minHeight),
|
||||
maxHeight: may(Integer.encode, param.maxHeight),
|
||||
minHeight: may(smallIntToApi, param.minHeight),
|
||||
maxHeight: may(smallIntToApi, param.maxHeight),
|
||||
};
|
||||
}
|
||||
|
||||
@ -39,8 +40,8 @@ interface RpcBlockSearchParams {
|
||||
function encodeBlockSearchParams(params: requests.BlockSearchParams): RpcBlockSearchParams {
|
||||
return {
|
||||
query: params.query,
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
order_by: params.order_by,
|
||||
};
|
||||
}
|
||||
@ -57,7 +58,7 @@ function encodeAbciQueryParams(params: requests.AbciQueryParams): RpcAbciQueryPa
|
||||
return {
|
||||
path: assertNotEmpty(params.path),
|
||||
data: toHex(params.data),
|
||||
height: may(Integer.encode, params.height),
|
||||
height: may(smallIntToApi, params.height),
|
||||
prove: params.prove,
|
||||
};
|
||||
}
|
||||
@ -95,8 +96,8 @@ function encodeTxSearchParams(params: requests.TxSearchParams): RpcTxSearchParam
|
||||
return {
|
||||
query: params.query,
|
||||
prove: params.prove,
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
order_by: params.order_by,
|
||||
};
|
||||
}
|
||||
@ -108,9 +109,9 @@ interface RpcValidatorsParams {
|
||||
}
|
||||
function encodeValidatorsParams(params: requests.ValidatorsParams): RpcValidatorsParams {
|
||||
return {
|
||||
height: may(Integer.encode, params.height),
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
height: may(smallIntToApi, params.height),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ describe("Adaptor Responses", () => {
|
||||
});
|
||||
expect(validator).toEqual({
|
||||
address: fromHex("A03DC128D38DB0BC5F18AE1872F1CB2E1FD41157"),
|
||||
votingPower: 169980,
|
||||
votingPower: BigInt(169980),
|
||||
pubkey: {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("2BX6Zuj8RmdJAkD1BAg6KB0v04liyM7jBdwOGIb9F9Q="),
|
||||
@ -72,7 +72,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="),
|
||||
},
|
||||
votingPower: 11418237,
|
||||
votingPower: BigInt(11418237),
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,7 +93,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("HjSC7VkhKih6xMhudlqfaFE8ZZnP8RKJPv4iqR7RhcE="),
|
||||
},
|
||||
votingPower: 0,
|
||||
votingPower: BigInt(0),
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -116,7 +116,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="),
|
||||
},
|
||||
votingPower: 11228980,
|
||||
votingPower: BigInt(11228980),
|
||||
proposerPriority: 62870960,
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { DateWithNanoseconds, fromRfc3339WithNanoseconds } from "../../dates";
|
||||
import { apiToBigInt, apiToSmallInt } from "../../inthelpers";
|
||||
import { SubscriptionEvent } from "../../rpcclients";
|
||||
import { BlockIdFlag, CommitSignature, ValidatorPubkey } from "../../types";
|
||||
import {
|
||||
@ -15,7 +16,6 @@ import {
|
||||
assertSet,
|
||||
assertString,
|
||||
dictionaryToStringMap,
|
||||
Integer,
|
||||
may,
|
||||
} from "../encodings";
|
||||
import { hashTx } from "../hasher";
|
||||
@ -35,7 +35,7 @@ interface RpcAbciInfoResponse {
|
||||
function decodeAbciInfo(data: RpcAbciInfoResponse): responses.AbciInfoResponse {
|
||||
return {
|
||||
data: data.data,
|
||||
lastBlockHeight: may(Integer.parse, data.last_block_height),
|
||||
lastBlockHeight: may(apiToSmallInt, data.last_block_height),
|
||||
lastBlockAppHash: may(fromBase64, data.last_block_app_hash),
|
||||
};
|
||||
}
|
||||
@ -96,10 +96,10 @@ function decodeAbciQuery(data: RpcAbciQueryResponse): responses.AbciQueryRespons
|
||||
key: fromBase64(assertString(data.key ?? "")),
|
||||
value: fromBase64(assertString(data.value ?? "")),
|
||||
proof: may(decodeQueryProof, data.proofOps),
|
||||
height: may(Integer.parse, data.height),
|
||||
code: may(Integer.parse, data.code),
|
||||
height: may(apiToSmallInt, data.height),
|
||||
code: may(apiToSmallInt, data.code),
|
||||
codespace: assertString(data.codespace ?? ""),
|
||||
index: may(Integer.parse, data.index),
|
||||
index: may(apiToSmallInt, data.index),
|
||||
log: data.log,
|
||||
info: assertString(data.info ?? ""),
|
||||
};
|
||||
@ -153,13 +153,13 @@ interface RpcTxData {
|
||||
|
||||
function decodeTxData(data: RpcTxData): responses.TxData {
|
||||
return {
|
||||
code: Integer.parse(assertNumber(data.code ?? 0)),
|
||||
code: apiToSmallInt(assertNumber(data.code ?? 0)),
|
||||
codeSpace: data.codespace,
|
||||
log: data.log,
|
||||
data: may(fromBase64, data.data),
|
||||
events: data.events ? decodeEvents(data.events) : [],
|
||||
gasWanted: Integer.parse(data.gas_wanted ?? "0"),
|
||||
gasUsed: Integer.parse(data.gas_used ?? "0"),
|
||||
gasWanted: apiToSmallInt(data.gas_wanted ?? "0"),
|
||||
gasUsed: apiToSmallInt(data.gas_used ?? "0"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -221,8 +221,8 @@ interface RpcBlockParams {
|
||||
*/
|
||||
function decodeBlockParams(data: RpcBlockParams): responses.BlockParams {
|
||||
return {
|
||||
maxBytes: Integer.parse(assertNotEmpty(data.max_bytes)),
|
||||
maxGas: Integer.parse(assertNotEmpty(data.max_gas)),
|
||||
maxBytes: apiToSmallInt(assertNotEmpty(data.max_bytes)),
|
||||
maxGas: apiToSmallInt(assertNotEmpty(data.max_gas)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -233,8 +233,8 @@ interface RpcEvidenceParams {
|
||||
|
||||
function decodeEvidenceParams(data: RpcEvidenceParams): responses.EvidenceParams {
|
||||
return {
|
||||
maxAgeNumBlocks: Integer.parse(assertNotEmpty(data.max_age_num_blocks)),
|
||||
maxAgeDuration: Integer.parse(assertNotEmpty(data.max_age_duration)),
|
||||
maxAgeNumBlocks: apiToSmallInt(assertNotEmpty(data.max_age_num_blocks)),
|
||||
maxAgeDuration: apiToSmallInt(assertNotEmpty(data.max_age_duration)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ interface RpcValidatorUpdate {
|
||||
export function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.ValidatorUpdate {
|
||||
return {
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(data.power ?? 0),
|
||||
votingPower: apiToBigInt(data.power ?? "0"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ interface RpcBlockResultsResponse {
|
||||
|
||||
function decodeBlockResults(data: RpcBlockResultsResponse): responses.BlockResultsResponse {
|
||||
return {
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
results: (data.txs_results || []).map(decodeTxData),
|
||||
validatorUpdates: (data.validator_updates || []).map(decodeValidatorUpdate),
|
||||
consensusUpdates: may(decodeConsensusParams, data.consensus_param_updates),
|
||||
@ -330,8 +330,8 @@ interface RpcBlockVersion {
|
||||
|
||||
function decodeBlockVersion(data: RpcBlockVersion): responses.Version {
|
||||
return {
|
||||
block: Integer.parse(data.block),
|
||||
app: Integer.parse(data.app ?? 0),
|
||||
block: apiToSmallInt(data.block),
|
||||
app: apiToSmallInt(data.app ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
@ -369,7 +369,7 @@ function decodeHeader(data: RpcHeader): responses.Header {
|
||||
return {
|
||||
version: decodeBlockVersion(data.version),
|
||||
chainId: assertNotEmpty(data.chain_id),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
time: fromRfc3339WithNanoseconds(assertNotEmpty(data.time)),
|
||||
|
||||
// When there is no last block ID (i.e. this block's height is 1), we get an empty structure like this:
|
||||
@ -400,9 +400,9 @@ interface RpcBlockMeta {
|
||||
function decodeBlockMeta(data: RpcBlockMeta): responses.BlockMeta {
|
||||
return {
|
||||
blockId: decodeBlockId(data.block_id),
|
||||
blockSize: Integer.parse(assertNotEmpty(data.block_size)),
|
||||
blockSize: apiToSmallInt(assertNotEmpty(data.block_size)),
|
||||
header: decodeHeader(data.header),
|
||||
numTxs: Integer.parse(assertNotEmpty(data.num_txs)),
|
||||
numTxs: apiToSmallInt(assertNotEmpty(data.num_txs)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -413,7 +413,7 @@ interface RpcBlockchainResponse {
|
||||
|
||||
function decodeBlockchain(data: RpcBlockchainResponse): responses.BlockchainResponse {
|
||||
return {
|
||||
lastHeight: Integer.parse(assertNotEmpty(data.last_height)),
|
||||
lastHeight: apiToSmallInt(assertNotEmpty(data.last_height)),
|
||||
blockMetas: assertArray(data.block_metas).map(decodeBlockMeta),
|
||||
};
|
||||
}
|
||||
@ -440,7 +440,7 @@ interface RpcBroadcastTxCommitResponse {
|
||||
|
||||
function decodeBroadcastTxCommit(data: RpcBroadcastTxCommitResponse): responses.BroadcastTxCommitResponse {
|
||||
return {
|
||||
height: Integer.parse(data.height),
|
||||
height: apiToSmallInt(data.height),
|
||||
hash: fromHex(assertNotEmpty(data.hash)),
|
||||
checkTx: decodeTxData(assertObject(data.check_tx)),
|
||||
deliverTx: may(decodeTxData, data.deliver_tx),
|
||||
@ -494,8 +494,8 @@ interface RpcCommit {
|
||||
function decodeCommit(data: RpcCommit): responses.Commit {
|
||||
return {
|
||||
blockId: decodeBlockId(assertObject(data.block_id)),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
round: Integer.parse(data.round),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
round: apiToSmallInt(data.round),
|
||||
signatures: assertArray(data.signatures).map(decodeCommitSignature),
|
||||
};
|
||||
}
|
||||
@ -528,7 +528,7 @@ export function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Val
|
||||
return {
|
||||
address: fromHex(assertNotEmpty(data.address)),
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(assertNotEmpty(data.power)),
|
||||
votingPower: apiToBigInt(assertNotEmpty(data.power)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -571,9 +571,9 @@ interface RpcValidatorInfo {
|
||||
export function decodeValidatorInfo(data: RpcValidatorInfo): responses.Validator {
|
||||
return {
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(assertNotEmpty(data.voting_power)),
|
||||
votingPower: apiToBigInt(assertNotEmpty(data.voting_power)),
|
||||
address: fromHex(assertNotEmpty(data.address)),
|
||||
proposerPriority: data.proposer_priority ? Integer.parse(data.proposer_priority) : undefined,
|
||||
proposerPriority: data.proposer_priority ? apiToSmallInt(data.proposer_priority) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@ -611,9 +611,9 @@ function decodeNodeInfo(data: RpcNodeInfo): responses.NodeInfo {
|
||||
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)),
|
||||
app: apiToSmallInt(assertNotEmpty(data.protocol_version.app)),
|
||||
block: apiToSmallInt(assertNotEmpty(data.protocol_version.block)),
|
||||
p2p: apiToSmallInt(assertNotEmpty(data.protocol_version.p2p)),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -633,7 +633,7 @@ function decodeSyncInfo(data: RpcSyncInfo): responses.SyncInfo {
|
||||
latestBlockHash: fromHex(assertNotEmpty(data.latest_block_hash)),
|
||||
latestAppHash: fromHex(assertNotEmpty(data.latest_app_hash)),
|
||||
latestBlockTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.latest_block_time)),
|
||||
latestBlockHeight: Integer.parse(assertNotEmpty(data.latest_block_height)),
|
||||
latestBlockHeight: apiToSmallInt(assertNotEmpty(data.latest_block_height)),
|
||||
catchingUp: assertBoolean(data.catching_up),
|
||||
};
|
||||
}
|
||||
@ -685,8 +685,8 @@ function decodeTxProof(data: RpcTxProof): responses.TxProof {
|
||||
data: fromBase64(assertNotEmpty(data.data)),
|
||||
rootHash: fromHex(assertNotEmpty(data.root_hash)),
|
||||
proof: {
|
||||
total: Integer.parse(assertNotEmpty(data.proof.total)),
|
||||
index: Integer.parse(assertNotEmpty(data.proof.index)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.proof.total)),
|
||||
index: apiToSmallInt(assertNotEmpty(data.proof.index)),
|
||||
leafHash: fromBase64(assertNotEmpty(data.proof.leaf_hash)),
|
||||
aunts: assertArray(data.proof.aunts).map(fromBase64),
|
||||
},
|
||||
@ -708,8 +708,8 @@ function decodeTxResponse(data: RpcTxResponse): responses.TxResponse {
|
||||
return {
|
||||
tx: fromBase64(assertNotEmpty(data.tx)),
|
||||
result: decodeTxData(assertObject(data.tx_result)),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
index: Integer.parse(assertNumber(data.index)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
index: apiToSmallInt(assertNumber(data.index)),
|
||||
hash: fromHex(assertNotEmpty(data.hash)),
|
||||
proof: may(decodeTxProof, data.proof),
|
||||
};
|
||||
@ -722,7 +722,7 @@ interface RpcTxSearchResponse {
|
||||
|
||||
function decodeTxSearch(data: RpcTxSearchResponse): responses.TxSearchResponse {
|
||||
return {
|
||||
totalCount: Integer.parse(assertNotEmpty(data.total_count)),
|
||||
totalCount: apiToSmallInt(assertNotEmpty(data.total_count)),
|
||||
txs: assertArray(data.txs).map(decodeTxResponse),
|
||||
};
|
||||
}
|
||||
@ -742,8 +742,8 @@ function decodeTxEvent(data: RpcTxEvent): responses.TxEvent {
|
||||
tx: tx,
|
||||
hash: hashTx(tx),
|
||||
result: decodeTxData(data.result),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
index: may(Integer.parse, data.index),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
index: may(apiToSmallInt, data.index),
|
||||
};
|
||||
}
|
||||
|
||||
@ -756,10 +756,10 @@ interface RpcValidatorsResponse {
|
||||
|
||||
function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse {
|
||||
return {
|
||||
blockHeight: Integer.parse(assertNotEmpty(data.block_height)),
|
||||
blockHeight: apiToSmallInt(assertNotEmpty(data.block_height)),
|
||||
validators: assertArray(data.validators).map(decodeValidatorInfo),
|
||||
count: Integer.parse(assertNotEmpty(data.count)),
|
||||
total: Integer.parse(assertNotEmpty(data.total)),
|
||||
count: apiToSmallInt(assertNotEmpty(data.count)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.total)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -813,7 +813,7 @@ interface RpcBlockSearchResponse {
|
||||
|
||||
function decodeBlockSearch(data: RpcBlockSearchResponse): responses.BlockSearchResponse {
|
||||
return {
|
||||
totalCount: Integer.parse(assertNotEmpty(data.total_count)),
|
||||
totalCount: apiToSmallInt(assertNotEmpty(data.total_count)),
|
||||
blocks: assertArray(data.blocks).map(decodeBlockResponse),
|
||||
};
|
||||
}
|
||||
@ -825,8 +825,8 @@ interface RpcNumUnconfirmedTxsResponse {
|
||||
|
||||
function decodeNumUnconfirmedTxs(data: RpcNumUnconfirmedTxsResponse): responses.NumUnconfirmedTxsResponse {
|
||||
return {
|
||||
total: Integer.parse(assertNotEmpty(data.total)),
|
||||
totalBytes: Integer.parse(assertNotEmpty(data.total_bytes)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.total)),
|
||||
totalBytes: apiToSmallInt(assertNotEmpty(data.total_bytes)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings";
|
||||
import {
|
||||
encodeBlockId,
|
||||
encodeBytes,
|
||||
encodeString,
|
||||
encodeTime,
|
||||
encodeUvarint,
|
||||
encodeVersion,
|
||||
} from "./encodings";
|
||||
|
||||
describe("encodings", () => {
|
||||
describe("encodeString", () => {
|
||||
@ -13,14 +20,14 @@ describe("encodings", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("encodeInt", () => {
|
||||
describe("encodeUvarint", () => {
|
||||
it("works", () => {
|
||||
expect(encodeInt(0)).toEqual(Uint8Array.from([0]));
|
||||
expect(encodeInt(1)).toEqual(Uint8Array.from([1]));
|
||||
expect(encodeInt(127)).toEqual(Uint8Array.from([127]));
|
||||
expect(encodeInt(128)).toEqual(Uint8Array.from([128, 1]));
|
||||
expect(encodeInt(255)).toEqual(Uint8Array.from([255, 1]));
|
||||
expect(encodeInt(256)).toEqual(Uint8Array.from([128, 2]));
|
||||
expect(encodeUvarint(0)).toEqual(Uint8Array.from([0]));
|
||||
expect(encodeUvarint(1)).toEqual(Uint8Array.from([1]));
|
||||
expect(encodeUvarint(127)).toEqual(Uint8Array.from([127]));
|
||||
expect(encodeUvarint(128)).toEqual(Uint8Array.from([128, 1]));
|
||||
expect(encodeUvarint(255)).toEqual(Uint8Array.from([255, 1]));
|
||||
expect(encodeUvarint(256)).toEqual(Uint8Array.from([128, 2]));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import { Int53 } from "@cosmjs/math";
|
||||
|
||||
import { ReadonlyDateWithNanoseconds } from "../dates";
|
||||
import { BlockId, Version } from "./responses";
|
||||
@ -140,17 +139,6 @@ export function dictionaryToStringMap(obj: Record<string, unknown>): Map<string,
|
||||
return out;
|
||||
}
|
||||
|
||||
export class Integer {
|
||||
public static parse(input: string | number): number {
|
||||
const asInt = typeof input === "number" ? new Int53(input) : Int53.fromString(input);
|
||||
return asInt.toNumber();
|
||||
}
|
||||
|
||||
public static encode(num: number): string {
|
||||
return new Int53(num).toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Encodings needed for hashing block headers
|
||||
// Several of these functions are inspired by https://github.com/nomic-io/js-tendermint/blob/tendermint-0.30/src/
|
||||
|
||||
@ -161,18 +149,21 @@ export function encodeString(s: string): Uint8Array {
|
||||
}
|
||||
|
||||
// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L79-L87
|
||||
export function encodeInt(n: number): Uint8Array {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return n >= 0x80 ? Uint8Array.from([(n & 0xff) | 0x80, ...encodeInt(n >> 7)]) : Uint8Array.from([n & 0xff]);
|
||||
export function encodeUvarint(n: number): Uint8Array {
|
||||
return n >= 0x80
|
||||
? // eslint-disable-next-line no-bitwise
|
||||
Uint8Array.from([(n & 0xff) | 0x80, ...encodeUvarint(n >> 7)])
|
||||
: // eslint-disable-next-line no-bitwise
|
||||
Uint8Array.from([n & 0xff]);
|
||||
}
|
||||
|
||||
// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L134-L178
|
||||
export function encodeTime(time: ReadonlyDateWithNanoseconds): Uint8Array {
|
||||
const milliseconds = time.getTime();
|
||||
const seconds = Math.floor(milliseconds / 1000);
|
||||
const secondsArray = seconds ? [0x08, ...encodeInt(seconds)] : new Uint8Array();
|
||||
const secondsArray = seconds ? [0x08, ...encodeUvarint(seconds)] : new Uint8Array();
|
||||
const nanoseconds = (time.nanoseconds || 0) + (milliseconds % 1000) * 1e6;
|
||||
const nanosecondsArray = nanoseconds ? [0x10, ...encodeInt(nanoseconds)] : new Uint8Array();
|
||||
const nanosecondsArray = nanoseconds ? [0x10, ...encodeUvarint(nanoseconds)] : new Uint8Array();
|
||||
return Uint8Array.from([...secondsArray, ...nanosecondsArray]);
|
||||
}
|
||||
|
||||
@ -184,8 +175,10 @@ export function encodeBytes(bytes: Uint8Array): Uint8Array {
|
||||
}
|
||||
|
||||
export function encodeVersion(version: Version): Uint8Array {
|
||||
const blockArray = version.block ? Uint8Array.from([0x08, ...encodeInt(version.block)]) : new Uint8Array();
|
||||
const appArray = version.app ? Uint8Array.from([0x10, ...encodeInt(version.app)]) : new Uint8Array();
|
||||
const blockArray = version.block
|
||||
? Uint8Array.from([0x08, ...encodeUvarint(version.block)])
|
||||
: new Uint8Array();
|
||||
const appArray = version.app ? Uint8Array.from([0x10, ...encodeUvarint(version.app)]) : new Uint8Array();
|
||||
return Uint8Array.from([...blockArray, ...appArray]);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import { Sha256, sha256 } from "@cosmjs/crypto";
|
||||
|
||||
import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings";
|
||||
import {
|
||||
encodeBlockId,
|
||||
encodeBytes,
|
||||
encodeString,
|
||||
encodeTime,
|
||||
encodeUvarint,
|
||||
encodeVersion,
|
||||
} from "./encodings";
|
||||
import { Header } from "./responses";
|
||||
|
||||
// hash is sha256
|
||||
@ -55,7 +62,7 @@ export function hashBlock(header: Header): Uint8Array {
|
||||
const encodedFields: readonly Uint8Array[] = [
|
||||
encodeVersion(header.version),
|
||||
encodeString(header.chainId),
|
||||
encodeInt(header.height),
|
||||
encodeUvarint(header.height),
|
||||
encodeTime(header.time),
|
||||
encodeBlockId(header.lastBlockId),
|
||||
|
||||
|
||||
@ -352,13 +352,13 @@ export interface SyncInfo {
|
||||
export interface Validator {
|
||||
readonly address: Uint8Array;
|
||||
readonly pubkey?: ValidatorPubkey;
|
||||
readonly votingPower: number;
|
||||
readonly votingPower: bigint;
|
||||
readonly proposerPriority?: number;
|
||||
}
|
||||
|
||||
export interface ValidatorUpdate {
|
||||
readonly pubkey: ValidatorPubkey;
|
||||
readonly votingPower: number;
|
||||
readonly votingPower: bigint;
|
||||
}
|
||||
|
||||
export interface ConsensusParams {
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
import { toBase64, toHex } from "@cosmjs/encoding";
|
||||
import { JsonRpcRequest } from "@cosmjs/json-rpc";
|
||||
|
||||
import { smallIntToApi } from "../../inthelpers";
|
||||
import { createJsonRpcRequest } from "../../jsonrpc";
|
||||
import { assertNotEmpty, Integer, may } from "../encodings";
|
||||
import { assertNotEmpty, may } from "../encodings";
|
||||
import * as requests from "../requests";
|
||||
|
||||
interface HeightParam {
|
||||
@ -14,7 +15,7 @@ interface RpcHeightParam {
|
||||
}
|
||||
function encodeHeightParam(param: HeightParam): RpcHeightParam {
|
||||
return {
|
||||
height: may(Integer.encode, param.height),
|
||||
height: may(smallIntToApi, param.height),
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,8 +26,8 @@ interface RpcBlockchainRequestParams {
|
||||
|
||||
function encodeBlockchainRequestParams(param: requests.BlockchainRequestParams): RpcBlockchainRequestParams {
|
||||
return {
|
||||
minHeight: may(Integer.encode, param.minHeight),
|
||||
maxHeight: may(Integer.encode, param.maxHeight),
|
||||
minHeight: may(smallIntToApi, param.minHeight),
|
||||
maxHeight: may(smallIntToApi, param.maxHeight),
|
||||
};
|
||||
}
|
||||
|
||||
@ -39,8 +40,8 @@ interface RpcBlockSearchParams {
|
||||
function encodeBlockSearchParams(params: requests.BlockSearchParams): RpcBlockSearchParams {
|
||||
return {
|
||||
query: params.query,
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
order_by: params.order_by,
|
||||
};
|
||||
}
|
||||
@ -57,7 +58,7 @@ function encodeAbciQueryParams(params: requests.AbciQueryParams): RpcAbciQueryPa
|
||||
return {
|
||||
path: assertNotEmpty(params.path),
|
||||
data: toHex(params.data),
|
||||
height: may(Integer.encode, params.height),
|
||||
height: may(smallIntToApi, params.height),
|
||||
prove: params.prove,
|
||||
};
|
||||
}
|
||||
@ -95,8 +96,8 @@ function encodeTxSearchParams(params: requests.TxSearchParams): RpcTxSearchParam
|
||||
return {
|
||||
query: params.query,
|
||||
prove: params.prove,
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
order_by: params.order_by,
|
||||
};
|
||||
}
|
||||
@ -108,9 +109,9 @@ interface RpcValidatorsParams {
|
||||
}
|
||||
function encodeValidatorsParams(params: requests.ValidatorsParams): RpcValidatorsParams {
|
||||
return {
|
||||
height: may(Integer.encode, params.height),
|
||||
page: may(Integer.encode, params.page),
|
||||
per_page: may(Integer.encode, params.per_page),
|
||||
height: may(smallIntToApi, params.height),
|
||||
page: may(smallIntToApi, params.page),
|
||||
per_page: may(smallIntToApi, params.per_page),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ describe("Adaptor Responses", () => {
|
||||
});
|
||||
expect(validator).toEqual({
|
||||
address: fromHex("A03DC128D38DB0BC5F18AE1872F1CB2E1FD41157"),
|
||||
votingPower: 169980,
|
||||
votingPower: BigInt(169980),
|
||||
pubkey: {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("2BX6Zuj8RmdJAkD1BAg6KB0v04liyM7jBdwOGIb9F9Q="),
|
||||
@ -72,7 +72,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="),
|
||||
},
|
||||
votingPower: 11418237,
|
||||
votingPower: BigInt(11418237),
|
||||
});
|
||||
});
|
||||
|
||||
@ -93,7 +93,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("HjSC7VkhKih6xMhudlqfaFE8ZZnP8RKJPv4iqR7RhcE="),
|
||||
},
|
||||
votingPower: 0,
|
||||
votingPower: BigInt(0),
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -116,7 +116,7 @@ describe("Adaptor Responses", () => {
|
||||
algorithm: "ed25519",
|
||||
data: fromBase64("0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI="),
|
||||
},
|
||||
votingPower: 11228980,
|
||||
votingPower: BigInt(11228980),
|
||||
proposerPriority: 62870960,
|
||||
});
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
|
||||
import { assert } from "@cosmjs/utils";
|
||||
|
||||
import { DateWithNanoseconds, fromRfc3339WithNanoseconds } from "../../dates";
|
||||
import { apiToBigInt, apiToSmallInt } from "../../inthelpers";
|
||||
import { SubscriptionEvent } from "../../rpcclients";
|
||||
import { BlockIdFlag, CommitSignature, ValidatorPubkey } from "../../types";
|
||||
import {
|
||||
@ -15,7 +16,6 @@ import {
|
||||
assertSet,
|
||||
assertString,
|
||||
dictionaryToStringMap,
|
||||
Integer,
|
||||
may,
|
||||
} from "../encodings";
|
||||
import { hashTx } from "../hasher";
|
||||
@ -35,7 +35,7 @@ interface RpcAbciInfoResponse {
|
||||
function decodeAbciInfo(data: RpcAbciInfoResponse): responses.AbciInfoResponse {
|
||||
return {
|
||||
data: data.data,
|
||||
lastBlockHeight: may(Integer.parse, data.last_block_height),
|
||||
lastBlockHeight: may(apiToSmallInt, data.last_block_height),
|
||||
lastBlockAppHash: may(fromBase64, data.last_block_app_hash),
|
||||
};
|
||||
}
|
||||
@ -95,10 +95,10 @@ function decodeAbciQuery(data: RpcAbciQueryResponse): responses.AbciQueryRespons
|
||||
key: fromBase64(assertString(data.key ?? "")),
|
||||
value: fromBase64(assertString(data.value ?? "")),
|
||||
proof: may(decodeQueryProof, data.proofOps),
|
||||
height: may(Integer.parse, data.height),
|
||||
code: may(Integer.parse, data.code),
|
||||
height: may(apiToSmallInt, data.height),
|
||||
code: may(apiToSmallInt, data.code),
|
||||
codespace: assertString(data.codespace ?? ""),
|
||||
index: may(Integer.parse, data.index),
|
||||
index: may(apiToSmallInt, data.index),
|
||||
log: data.log,
|
||||
info: assertString(data.info ?? ""),
|
||||
};
|
||||
@ -154,13 +154,13 @@ interface RpcTxData {
|
||||
|
||||
function decodeTxData(data: RpcTxData): responses.TxData {
|
||||
return {
|
||||
code: Integer.parse(assertNumber(data.code ?? 0)),
|
||||
code: apiToSmallInt(assertNumber(data.code ?? 0)),
|
||||
codeSpace: data.codespace,
|
||||
log: data.log,
|
||||
data: may(fromBase64, data.data),
|
||||
events: data.events ? decodeEvents(data.events) : [],
|
||||
gasWanted: Integer.parse(data.gas_wanted ?? "0"),
|
||||
gasUsed: Integer.parse(data.gas_used ?? "0"),
|
||||
gasWanted: apiToSmallInt(data.gas_wanted ?? "0"),
|
||||
gasUsed: apiToSmallInt(data.gas_used ?? "0"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -222,8 +222,8 @@ interface RpcBlockParams {
|
||||
*/
|
||||
function decodeBlockParams(data: RpcBlockParams): responses.BlockParams {
|
||||
return {
|
||||
maxBytes: Integer.parse(assertNotEmpty(data.max_bytes)),
|
||||
maxGas: Integer.parse(assertNotEmpty(data.max_gas)),
|
||||
maxBytes: apiToSmallInt(assertNotEmpty(data.max_bytes)),
|
||||
maxGas: apiToSmallInt(assertNotEmpty(data.max_gas)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -234,8 +234,8 @@ interface RpcEvidenceParams {
|
||||
|
||||
function decodeEvidenceParams(data: RpcEvidenceParams): responses.EvidenceParams {
|
||||
return {
|
||||
maxAgeNumBlocks: Integer.parse(assertNotEmpty(data.max_age_num_blocks)),
|
||||
maxAgeDuration: Integer.parse(assertNotEmpty(data.max_age_duration)),
|
||||
maxAgeNumBlocks: apiToSmallInt(assertNotEmpty(data.max_age_num_blocks)),
|
||||
maxAgeDuration: apiToSmallInt(assertNotEmpty(data.max_age_duration)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ interface RpcValidatorUpdate {
|
||||
export function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.ValidatorUpdate {
|
||||
return {
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(data.power ?? 0),
|
||||
votingPower: apiToBigInt(data.power ?? "0"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ interface RpcBlockResultsResponse {
|
||||
|
||||
function decodeBlockResults(data: RpcBlockResultsResponse): responses.BlockResultsResponse {
|
||||
return {
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
results: (data.txs_results || []).map(decodeTxData),
|
||||
validatorUpdates: (data.validator_updates || []).map(decodeValidatorUpdate),
|
||||
consensusUpdates: may(decodeConsensusParams, data.consensus_param_updates),
|
||||
@ -331,8 +331,8 @@ interface RpcBlockVersion {
|
||||
|
||||
function decodeBlockVersion(data: RpcBlockVersion): responses.Version {
|
||||
return {
|
||||
block: Integer.parse(data.block),
|
||||
app: Integer.parse(data.app ?? 0),
|
||||
block: apiToSmallInt(data.block),
|
||||
app: apiToSmallInt(data.app ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
@ -370,7 +370,7 @@ function decodeHeader(data: RpcHeader): responses.Header {
|
||||
return {
|
||||
version: decodeBlockVersion(data.version),
|
||||
chainId: assertNotEmpty(data.chain_id),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
time: fromRfc3339WithNanoseconds(assertNotEmpty(data.time)),
|
||||
|
||||
// When there is no last block ID (i.e. this block's height is 1), we get an empty structure like this:
|
||||
@ -401,9 +401,9 @@ interface RpcBlockMeta {
|
||||
function decodeBlockMeta(data: RpcBlockMeta): responses.BlockMeta {
|
||||
return {
|
||||
blockId: decodeBlockId(data.block_id),
|
||||
blockSize: Integer.parse(assertNotEmpty(data.block_size)),
|
||||
blockSize: apiToSmallInt(assertNotEmpty(data.block_size)),
|
||||
header: decodeHeader(data.header),
|
||||
numTxs: Integer.parse(assertNotEmpty(data.num_txs)),
|
||||
numTxs: apiToSmallInt(assertNotEmpty(data.num_txs)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -414,7 +414,7 @@ interface RpcBlockchainResponse {
|
||||
|
||||
function decodeBlockchain(data: RpcBlockchainResponse): responses.BlockchainResponse {
|
||||
return {
|
||||
lastHeight: Integer.parse(assertNotEmpty(data.last_height)),
|
||||
lastHeight: apiToSmallInt(assertNotEmpty(data.last_height)),
|
||||
blockMetas: assertArray(data.block_metas).map(decodeBlockMeta),
|
||||
};
|
||||
}
|
||||
@ -441,7 +441,7 @@ interface RpcBroadcastTxCommitResponse {
|
||||
|
||||
function decodeBroadcastTxCommit(data: RpcBroadcastTxCommitResponse): responses.BroadcastTxCommitResponse {
|
||||
return {
|
||||
height: Integer.parse(data.height),
|
||||
height: apiToSmallInt(data.height),
|
||||
hash: fromHex(assertNotEmpty(data.hash)),
|
||||
checkTx: decodeTxData(assertObject(data.check_tx)),
|
||||
deliverTx: may(decodeTxData, data.deliver_tx),
|
||||
@ -495,8 +495,8 @@ interface RpcCommit {
|
||||
function decodeCommit(data: RpcCommit): responses.Commit {
|
||||
return {
|
||||
blockId: decodeBlockId(assertObject(data.block_id)),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
round: Integer.parse(data.round),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
round: apiToSmallInt(data.round),
|
||||
signatures: assertArray(data.signatures).map(decodeCommitSignature),
|
||||
};
|
||||
}
|
||||
@ -529,7 +529,7 @@ export function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Val
|
||||
return {
|
||||
address: fromHex(assertNotEmpty(data.address)),
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(assertNotEmpty(data.power)),
|
||||
votingPower: apiToBigInt(assertNotEmpty(data.power)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -572,9 +572,9 @@ interface RpcValidatorInfo {
|
||||
export function decodeValidatorInfo(data: RpcValidatorInfo): responses.Validator {
|
||||
return {
|
||||
pubkey: decodePubkey(assertObject(data.pub_key)),
|
||||
votingPower: Integer.parse(assertNotEmpty(data.voting_power)),
|
||||
votingPower: apiToBigInt(assertNotEmpty(data.voting_power)),
|
||||
address: fromHex(assertNotEmpty(data.address)),
|
||||
proposerPriority: data.proposer_priority ? Integer.parse(data.proposer_priority) : undefined,
|
||||
proposerPriority: data.proposer_priority ? apiToSmallInt(data.proposer_priority) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@ -612,9 +612,9 @@ function decodeNodeInfo(data: RpcNodeInfo): responses.NodeInfo {
|
||||
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)),
|
||||
app: apiToSmallInt(assertNotEmpty(data.protocol_version.app)),
|
||||
block: apiToSmallInt(assertNotEmpty(data.protocol_version.block)),
|
||||
p2p: apiToSmallInt(assertNotEmpty(data.protocol_version.p2p)),
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -634,7 +634,7 @@ function decodeSyncInfo(data: RpcSyncInfo): responses.SyncInfo {
|
||||
latestBlockHash: fromHex(assertNotEmpty(data.latest_block_hash)),
|
||||
latestAppHash: fromHex(assertNotEmpty(data.latest_app_hash)),
|
||||
latestBlockTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.latest_block_time)),
|
||||
latestBlockHeight: Integer.parse(assertNotEmpty(data.latest_block_height)),
|
||||
latestBlockHeight: apiToSmallInt(assertNotEmpty(data.latest_block_height)),
|
||||
catchingUp: assertBoolean(data.catching_up),
|
||||
};
|
||||
}
|
||||
@ -686,8 +686,8 @@ function decodeTxProof(data: RpcTxProof): responses.TxProof {
|
||||
data: fromBase64(assertNotEmpty(data.data)),
|
||||
rootHash: fromHex(assertNotEmpty(data.root_hash)),
|
||||
proof: {
|
||||
total: Integer.parse(assertNotEmpty(data.proof.total)),
|
||||
index: Integer.parse(assertNotEmpty(data.proof.index)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.proof.total)),
|
||||
index: apiToSmallInt(assertNotEmpty(data.proof.index)),
|
||||
leafHash: fromBase64(assertNotEmpty(data.proof.leaf_hash)),
|
||||
aunts: assertArray(data.proof.aunts).map(fromBase64),
|
||||
},
|
||||
@ -709,8 +709,8 @@ function decodeTxResponse(data: RpcTxResponse): responses.TxResponse {
|
||||
return {
|
||||
tx: fromBase64(assertNotEmpty(data.tx)),
|
||||
result: decodeTxData(assertObject(data.tx_result)),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
index: Integer.parse(assertNumber(data.index)),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
index: apiToSmallInt(assertNumber(data.index)),
|
||||
hash: fromHex(assertNotEmpty(data.hash)),
|
||||
proof: may(decodeTxProof, data.proof),
|
||||
};
|
||||
@ -723,7 +723,7 @@ interface RpcTxSearchResponse {
|
||||
|
||||
function decodeTxSearch(data: RpcTxSearchResponse): responses.TxSearchResponse {
|
||||
return {
|
||||
totalCount: Integer.parse(assertNotEmpty(data.total_count)),
|
||||
totalCount: apiToSmallInt(assertNotEmpty(data.total_count)),
|
||||
txs: assertArray(data.txs).map(decodeTxResponse),
|
||||
};
|
||||
}
|
||||
@ -743,8 +743,8 @@ function decodeTxEvent(data: RpcTxEvent): responses.TxEvent {
|
||||
tx: tx,
|
||||
hash: hashTx(tx),
|
||||
result: decodeTxData(data.result),
|
||||
height: Integer.parse(assertNotEmpty(data.height)),
|
||||
index: may(Integer.parse, data.index),
|
||||
height: apiToSmallInt(assertNotEmpty(data.height)),
|
||||
index: may(apiToSmallInt, data.index),
|
||||
};
|
||||
}
|
||||
|
||||
@ -757,10 +757,10 @@ interface RpcValidatorsResponse {
|
||||
|
||||
function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse {
|
||||
return {
|
||||
blockHeight: Integer.parse(assertNotEmpty(data.block_height)),
|
||||
blockHeight: apiToSmallInt(assertNotEmpty(data.block_height)),
|
||||
validators: assertArray(data.validators).map(decodeValidatorInfo),
|
||||
count: Integer.parse(assertNotEmpty(data.count)),
|
||||
total: Integer.parse(assertNotEmpty(data.total)),
|
||||
count: apiToSmallInt(assertNotEmpty(data.count)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.total)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -814,7 +814,7 @@ interface RpcBlockSearchResponse {
|
||||
|
||||
function decodeBlockSearch(data: RpcBlockSearchResponse): responses.BlockSearchResponse {
|
||||
return {
|
||||
totalCount: Integer.parse(assertNotEmpty(data.total_count)),
|
||||
totalCount: apiToSmallInt(assertNotEmpty(data.total_count)),
|
||||
blocks: assertArray(data.blocks).map(decodeBlockResponse),
|
||||
};
|
||||
}
|
||||
@ -826,8 +826,8 @@ interface RpcNumUnconfirmedTxsResponse {
|
||||
|
||||
function decodeNumUnconfirmedTxs(data: RpcNumUnconfirmedTxsResponse): responses.NumUnconfirmedTxsResponse {
|
||||
return {
|
||||
total: Integer.parse(assertNotEmpty(data.total)),
|
||||
totalBytes: Integer.parse(assertNotEmpty(data.total_bytes)),
|
||||
total: apiToSmallInt(assertNotEmpty(data.total)),
|
||||
totalBytes: apiToSmallInt(assertNotEmpty(data.total_bytes)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import { ReadonlyDate } from "readonly-date";
|
||||
|
||||
import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings";
|
||||
import {
|
||||
encodeBlockId,
|
||||
encodeBytes,
|
||||
encodeString,
|
||||
encodeTime,
|
||||
encodeUvarint,
|
||||
encodeVersion,
|
||||
} from "./encodings";
|
||||
|
||||
describe("encodings", () => {
|
||||
describe("encodeString", () => {
|
||||
@ -13,14 +20,14 @@ describe("encodings", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("encodeInt", () => {
|
||||
describe("encodeUvarint", () => {
|
||||
it("works", () => {
|
||||
expect(encodeInt(0)).toEqual(Uint8Array.from([0]));
|
||||
expect(encodeInt(1)).toEqual(Uint8Array.from([1]));
|
||||
expect(encodeInt(127)).toEqual(Uint8Array.from([127]));
|
||||
expect(encodeInt(128)).toEqual(Uint8Array.from([128, 1]));
|
||||
expect(encodeInt(255)).toEqual(Uint8Array.from([255, 1]));
|
||||
expect(encodeInt(256)).toEqual(Uint8Array.from([128, 2]));
|
||||
expect(encodeUvarint(0)).toEqual(Uint8Array.from([0]));
|
||||
expect(encodeUvarint(1)).toEqual(Uint8Array.from([1]));
|
||||
expect(encodeUvarint(127)).toEqual(Uint8Array.from([127]));
|
||||
expect(encodeUvarint(128)).toEqual(Uint8Array.from([128, 1]));
|
||||
expect(encodeUvarint(255)).toEqual(Uint8Array.from([255, 1]));
|
||||
expect(encodeUvarint(256)).toEqual(Uint8Array.from([128, 2]));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { toUtf8 } from "@cosmjs/encoding";
|
||||
import { Int53 } from "@cosmjs/math";
|
||||
|
||||
import { ReadonlyDateWithNanoseconds } from "../dates";
|
||||
import { BlockId, Version } from "./responses";
|
||||
@ -140,17 +139,6 @@ export function dictionaryToStringMap(obj: Record<string, unknown>): Map<string,
|
||||
return out;
|
||||
}
|
||||
|
||||
export class Integer {
|
||||
public static parse(input: string | number): number {
|
||||
const asInt = typeof input === "number" ? new Int53(input) : Int53.fromString(input);
|
||||
return asInt.toNumber();
|
||||
}
|
||||
|
||||
public static encode(num: number): string {
|
||||
return new Int53(num).toString();
|
||||
}
|
||||
}
|
||||
|
||||
// Encodings needed for hashing block headers
|
||||
// Several of these functions are inspired by https://github.com/nomic-io/js-tendermint/blob/tendermint-0.30/src/
|
||||
|
||||
@ -161,18 +149,21 @@ export function encodeString(s: string): Uint8Array {
|
||||
}
|
||||
|
||||
// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L79-L87
|
||||
export function encodeInt(n: number): Uint8Array {
|
||||
// eslint-disable-next-line no-bitwise
|
||||
return n >= 0x80 ? Uint8Array.from([(n & 0xff) | 0x80, ...encodeInt(n >> 7)]) : Uint8Array.from([n & 0xff]);
|
||||
export function encodeUvarint(n: number): Uint8Array {
|
||||
return n >= 0x80
|
||||
? // eslint-disable-next-line no-bitwise
|
||||
Uint8Array.from([(n & 0xff) | 0x80, ...encodeUvarint(n >> 7)])
|
||||
: // eslint-disable-next-line no-bitwise
|
||||
Uint8Array.from([n & 0xff]);
|
||||
}
|
||||
|
||||
// See https://github.com/tendermint/go-amino/blob/v0.15.0/encoder.go#L134-L178
|
||||
export function encodeTime(time: ReadonlyDateWithNanoseconds): Uint8Array {
|
||||
const milliseconds = time.getTime();
|
||||
const seconds = Math.floor(milliseconds / 1000);
|
||||
const secondsArray = seconds ? [0x08, ...encodeInt(seconds)] : new Uint8Array();
|
||||
const secondsArray = seconds ? [0x08, ...encodeUvarint(seconds)] : new Uint8Array();
|
||||
const nanoseconds = (time.nanoseconds || 0) + (milliseconds % 1000) * 1e6;
|
||||
const nanosecondsArray = nanoseconds ? [0x10, ...encodeInt(nanoseconds)] : new Uint8Array();
|
||||
const nanosecondsArray = nanoseconds ? [0x10, ...encodeUvarint(nanoseconds)] : new Uint8Array();
|
||||
return Uint8Array.from([...secondsArray, ...nanosecondsArray]);
|
||||
}
|
||||
|
||||
@ -184,8 +175,10 @@ export function encodeBytes(bytes: Uint8Array): Uint8Array {
|
||||
}
|
||||
|
||||
export function encodeVersion(version: Version): Uint8Array {
|
||||
const blockArray = version.block ? Uint8Array.from([0x08, ...encodeInt(version.block)]) : new Uint8Array();
|
||||
const appArray = version.app ? Uint8Array.from([0x10, ...encodeInt(version.app)]) : new Uint8Array();
|
||||
const blockArray = version.block
|
||||
? Uint8Array.from([0x08, ...encodeUvarint(version.block)])
|
||||
: new Uint8Array();
|
||||
const appArray = version.app ? Uint8Array.from([0x10, ...encodeUvarint(version.app)]) : new Uint8Array();
|
||||
return Uint8Array.from([...blockArray, ...appArray]);
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
import { Sha256, sha256 } from "@cosmjs/crypto";
|
||||
|
||||
import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings";
|
||||
import {
|
||||
encodeBlockId,
|
||||
encodeBytes,
|
||||
encodeString,
|
||||
encodeTime,
|
||||
encodeUvarint,
|
||||
encodeVersion,
|
||||
} from "./encodings";
|
||||
import { Header } from "./responses";
|
||||
|
||||
// hash is sha256
|
||||
@ -55,7 +62,7 @@ export function hashBlock(header: Header): Uint8Array {
|
||||
const encodedFields: readonly Uint8Array[] = [
|
||||
encodeVersion(header.version),
|
||||
encodeString(header.chainId),
|
||||
encodeInt(header.height),
|
||||
encodeUvarint(header.height),
|
||||
encodeTime(header.time),
|
||||
encodeBlockId(header.lastBlockId),
|
||||
|
||||
|
||||
@ -357,13 +357,13 @@ export interface SyncInfo {
|
||||
export interface Validator {
|
||||
readonly address: Uint8Array;
|
||||
readonly pubkey?: ValidatorPubkey;
|
||||
readonly votingPower: number;
|
||||
readonly votingPower: bigint;
|
||||
readonly proposerPriority?: number;
|
||||
}
|
||||
|
||||
export interface ValidatorUpdate {
|
||||
readonly pubkey: ValidatorPubkey;
|
||||
readonly votingPower: number;
|
||||
readonly votingPower: bigint;
|
||||
}
|
||||
|
||||
export interface ConsensusParams {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user