diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ad03bd1c..8654801af3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,12 @@ and this project adheres to - @cosmjs/stargate: Let `calculateFee` handle fee amounts that exceed the safe integer range. +### Fixed + +- @cosmjs/tendermint-rpc: Fix block results validator update decoder. ([#1151]) + +[#1151]: https://github.com/cosmos/cosmjs/issues/1151 + ## [0.28.4] - 2022-04-15 ### Added diff --git a/packages/tendermint-rpc/src/tendermint34/adaptor/responses.spec.ts b/packages/tendermint-rpc/src/tendermint34/adaptor/responses.spec.ts new file mode 100644 index 0000000000..a0de568c81 --- /dev/null +++ b/packages/tendermint-rpc/src/tendermint34/adaptor/responses.spec.ts @@ -0,0 +1,51 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { decodeValidatorGenesis, decodeValidatorInfo, decodeValidatorUpdate } from "./responses"; + +describe("Adaptor Responses", () => { + describe("decodeValidatorGenesis", () => { + it("works for genesis format", () => { + // from https://raw.githubusercontent.com/cosmos/mainnet/master/genesis.json + decodeValidatorGenesis({ + address: "A03DC128D38DB0BC5F18AE1872F1CB2E1FD41157", + name: "真本聪&IOSG", + power: "169980", + pub_key: { + type: "tendermint/PubKeyEd25519", + value: "2BX6Zuj8RmdJAkD1BAg6KB0v04liyM7jBdwOGIb9F9Q=", + }, + }); + }); + }); + + describe("decodeValidatorUpdate", () => { + it("works for block results format", () => { + // from https://rpc.cosmos.network/block_results?height=10539773 + decodeValidatorUpdate({ + pub_key: { + Sum: { + type: "tendermint.crypto.PublicKey_Ed25519", + value: { + ed25519: "0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI=", + }, + }, + }, + power: "11418237", + }); + }); + }); + + describe("decodeValidatorInfo", () => { + it("works for validators format", () => { + // from https://rpc.cosmos.network/validators?height=10601034 + decodeValidatorInfo({ + address: "AC2D56057CD84765E6FBE318979093E8E44AA18F", + pub_key: { + type: "tendermint/PubKeyEd25519", + value: "0kNlxBMpm+5WtfHIG1xsWatOXTKPLtmSqn3EiEIDZeI=", + }, + voting_power: "11228980", + proposer_priority: "62870960", + }); + }); + }); +}); diff --git a/packages/tendermint-rpc/src/tendermint34/adaptor/responses.ts b/packages/tendermint-rpc/src/tendermint34/adaptor/responses.ts index 62ab4e09f6..d76fbce23b 100644 --- a/packages/tendermint-rpc/src/tendermint34/adaptor/responses.ts +++ b/packages/tendermint-rpc/src/tendermint34/adaptor/responses.ts @@ -148,49 +148,51 @@ function decodeTxData(data: RpcTxData): responses.TxData { }; } -// yes, a different format for status and dump consensus state -interface RpcPubkey { - readonly type: string; - /** base64 encoded */ - readonly value: string; -} +type RpcPubkey = + | { + readonly type: string; + /** base64 encoded */ + readonly value: string; + } + | { + // See: https://github.com/cosmos/cosmjs/issues/1142 + readonly Sum: { + readonly type: string; + readonly value: { + /** base64 encoded */ + [algorithm: string]: string; + }; + }; + }; function decodePubkey(data: RpcPubkey): ValidatorPubkey { - switch (data.type) { - // go-amino special code - case "tendermint/PubKeyEd25519": - return { - algorithm: "ed25519", - data: fromBase64(assertNotEmpty(data.value)), - }; - case "tendermint/PubKeySecp256k1": - return { - algorithm: "secp256k1", - data: fromBase64(assertNotEmpty(data.value)), - }; - default: - throw new Error(`unknown pubkey type: ${data.type}`); + if ("Sum" in data) { + // we don't need to check type because we're checking algorithm + const [[algorithm, value]] = Object.entries(data.Sum.value); + assert(algorithm === "ed25519" || algorithm === "secp256k1", `unknown pubkey type: ${algorithm}`); + return { + algorithm, + data: fromBase64(assertNotEmpty(value)), + }; + } else { + switch (data.type) { + // go-amino special code + case "tendermint/PubKeyEd25519": + return { + algorithm: "ed25519", + data: fromBase64(assertNotEmpty(data.value)), + }; + case "tendermint/PubKeySecp256k1": + return { + algorithm: "secp256k1", + data: fromBase64(assertNotEmpty(data.value)), + }; + default: + throw new Error(`unknown pubkey type: ${data.type}`); + } } } -// for evidence, block results, etc. -interface RpcValidatorUpdate { - /** hex encoded */ - readonly address: string; - readonly pub_key: RpcPubkey; - readonly voting_power: string; - readonly proposer_priority: string; -} - -function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.Validator { - return { - pubkey: decodePubkey(assertObject(data.pub_key)), - votingPower: Integer.parse(assertNotEmpty(data.voting_power)), - address: fromHex(assertNotEmpty(data.address)), - proposerPriority: Integer.parse(data.proposer_priority), - }; -} - interface RpcBlockParams { readonly max_bytes: string; readonly max_gas: string; @@ -252,6 +254,19 @@ function decodeConsensusParams(data: RpcConsensusParams): responses.ConsensusPar }; } +// for block results +interface RpcValidatorUpdate { + readonly pub_key: RpcPubkey; + readonly power: string; +} + +export function decodeValidatorUpdate(data: RpcValidatorUpdate): responses.ValidatorUpdate { + return { + pubkey: decodePubkey(assertObject(data.pub_key)), + votingPower: Integer.parse(assertNotEmpty(data.power)), + }; +} + interface RpcBlockResultsResponse { readonly height: string; readonly txs_results: readonly RpcTxData[] | null; @@ -493,7 +508,7 @@ interface RpcValidatorGenesis { readonly name?: string; } -function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Validator { +export function decodeValidatorGenesis(data: RpcValidatorGenesis): responses.Validator { return { address: fromHex(assertNotEmpty(data.address)), pubkey: decodePubkey(assertObject(data.pub_key)), @@ -534,13 +549,15 @@ interface RpcValidatorInfo { readonly address: string; readonly pub_key: RpcPubkey; readonly voting_power: string; + readonly proposer_priority?: string; } -function decodeValidatorInfo(data: RpcValidatorInfo): responses.Validator { +export 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)), + proposerPriority: data.proposer_priority ? Integer.parse(data.proposer_priority) : undefined, }; } @@ -716,7 +733,7 @@ function decodeTxEvent(data: RpcTxEvent): responses.TxEvent { interface RpcValidatorsResponse { readonly block_height: string; - readonly validators: readonly RpcValidatorUpdate[]; + readonly validators: readonly RpcValidatorInfo[]; readonly count: string; readonly total: string; } @@ -724,7 +741,7 @@ interface RpcValidatorsResponse { function decodeValidators(data: RpcValidatorsResponse): responses.ValidatorsResponse { return { blockHeight: Integer.parse(assertNotEmpty(data.block_height)), - validators: assertArray(data.validators).map(decodeValidatorUpdate), + validators: assertArray(data.validators).map(decodeValidatorInfo), count: Integer.parse(assertNotEmpty(data.count)), total: Integer.parse(assertNotEmpty(data.total)), }; diff --git a/packages/tendermint-rpc/src/tendermint34/responses.ts b/packages/tendermint-rpc/src/tendermint34/responses.ts index e683fe45a8..ed38c9154d 100644 --- a/packages/tendermint-rpc/src/tendermint34/responses.ts +++ b/packages/tendermint-rpc/src/tendermint34/responses.ts @@ -56,7 +56,7 @@ export interface BlockResponse { export interface BlockResultsResponse { readonly height: number; readonly results: readonly TxData[]; - readonly validatorUpdates: readonly Validator[]; + readonly validatorUpdates: readonly ValidatorUpdate[]; readonly consensusUpdates?: ConsensusParams; readonly beginBlockEvents: readonly Event[]; readonly endBlockEvents: readonly Event[]; @@ -351,6 +351,11 @@ export interface Validator { readonly proposerPriority?: number; } +export interface ValidatorUpdate { + readonly pubkey: ValidatorPubkey; + readonly votingPower: number; +} + export interface ConsensusParams { readonly block: BlockParams; readonly evidence: EvidenceParams;