Merge pull request #775 from cosmos/add-decodeTxRaw

Add decodeTxRaw
This commit is contained in:
Simon Warta 2021-04-27 16:45:57 +02:00 committed by GitHub
commit 8eaa3687e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 85 additions and 47 deletions

View File

@ -87,6 +87,8 @@ and this project adheres to
- @cosmjs/stargate: Add transfer queries codec, as well as transfer query
methods to IBC query extension.
- @cosmjs/tendermint-rpc: Export `ValidatorSecp256k1Pubkey` interface.
- @cosmjs/proto-signing: Add transaction decoder `decodeTxRaw` for decoding
transaction bytes returned by Tendermint (e.g. in `IndexedTx.tx`).
### Changed

View File

@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { fromBase64, toBase64 } from "@cosmjs/encoding";
import {
decodeTxRaw,
DirectSecp256k1HdWallet,
encodePubkey,
makeAuthInfoBytes,
@ -16,7 +17,7 @@ import {
isBroadcastTxSuccess,
isMsgSendEncodeObject,
} from "@cosmjs/stargate";
import { Tx, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
import { TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
import { assert, sleep } from "@cosmjs/utils";
import { CosmWasmClient } from "./cosmwasmclient";
@ -232,8 +233,8 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages.filter((msg) => {
const tx = decodeTxRaw(result.tx);
const filteredMsgs = tx.body.messages.filter((msg) => {
if (!isMsgSendEncodeObject(msg)) return false;
const decoded = registry.decode(msg);
return decoded.fromAddress === sendSuccessful?.sender;
@ -260,8 +261,8 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages.filter((msg) => {
const tx = decodeTxRaw(result.tx);
const filteredMsgs = tx.body.messages.filter((msg) => {
if (!isMsgSendEncodeObject(msg)) return false;
const decoded = registry.decode(msg);
return decoded.toAddress === sendSuccessful?.recipient;
@ -346,8 +347,8 @@ describe("CosmWasmClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const msg = fromOneElementArray(tx.body!.messages);
const tx = decodeTxRaw(result.tx);
const msg = fromOneElementArray(tx.body.messages);
expect(msg.typeUrl).toEqual("/cosmos.bank.v1beta1.MsgSend");
const decoded = registry.decode(msg);
expect(decoded.toAddress).toEqual(sendSuccessful.recipient);

View File

@ -3,7 +3,7 @@ import { Secp256k1HdWallet } from "@cosmjs/amino";
import { UploadMeta } from "@cosmjs/cosmwasm-launchpad";
import { sha256 } from "@cosmjs/crypto";
import { toHex } from "@cosmjs/encoding";
import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing";
import { decodeTxRaw, DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing";
import {
AminoMsgDelegate,
AminoTypes,
@ -17,7 +17,7 @@ import {
import { DeepPartial, MsgSend } from "@cosmjs/stargate/build/codec/cosmos/bank/v1beta1/tx";
import { Coin } from "@cosmjs/stargate/build/codec/cosmos/base/v1beta1/coin";
import { MsgDelegate } from "@cosmjs/stargate/build/codec/cosmos/staking/v1beta1/tx";
import { AuthInfo, Tx, TxBody, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
import { AuthInfo, TxBody, TxRaw } from "@cosmjs/stargate/build/codec/cosmos/tx/v1beta1/tx";
import { assert, sleep } from "@cosmjs/utils";
import Long from "long";
import pako from "pako";
@ -601,11 +601,11 @@ describe("SigningCosmWasmClient", () => {
const searchResult = await client.getTx(result.transactionHash);
assert(searchResult, "Must find transaction");
const tx = Tx.decode(searchResult.tx);
const tx = decodeTxRaw(searchResult.tx);
// From ModifyingDirectSecp256k1HdWallet
expect(tx.body!.memo).toEqual("This was modified");
expect({ ...tx.authInfo!.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo!.fee!.gasLimit.toNumber()).toEqual(333333);
expect(tx.body.memo).toEqual("This was modified");
expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
});
});
@ -830,11 +830,11 @@ describe("SigningCosmWasmClient", () => {
const searchResult = await client.getTx(result.transactionHash);
assert(searchResult, "Must find transaction");
const tx = Tx.decode(searchResult.tx);
const tx = decodeTxRaw(searchResult.tx);
// From ModifyingSecp256k1HdWallet
expect(tx.body!.memo).toEqual("This was modified");
expect({ ...tx.authInfo!.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo!.fee!.gasLimit.toNumber()).toEqual(333333);
expect(tx.body.memo).toEqual("This was modified");
expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
});
});
});

View File

@ -0,0 +1,19 @@
import { AuthInfo, TxBody, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
export interface DecodedTxRaw {
readonly authInfo: AuthInfo;
readonly body: TxBody;
readonly signatures: readonly Uint8Array[];
}
/**
* Takes a serialized TxRaw (the bytes stored in Tendermint) and decodes it into something usable.
*/
export function decodeTxRaw(tx: Uint8Array): DecodedTxRaw {
const txRaw = TxRaw.decode(tx);
return {
authInfo: AuthInfo.decode(txRaw.authInfoBytes),
body: TxBody.decode(txRaw.bodyBytes),
signatures: txRaw.signatures,
};
}

View File

@ -1,6 +1,7 @@
// This type happens to be shared between Amino and Direct sign modes
export { Coin, coin, coins, parseCoins } from "@cosmjs/amino";
export { decodeTxRaw, DecodedTxRaw } from "./decode";
export {
DecodeObject,
EncodeObject,

View File

@ -2,7 +2,8 @@
import { fromBase64, fromHex, toHex } from "@cosmjs/encoding";
import { SignMode } from "./codec/cosmos/tx/signing/v1beta1/signing";
import { Tx, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { decodeTxRaw } from "./decode";
import { DirectSecp256k1HdWallet } from "./directsecp256k1hdwallet";
import { Registry } from "./registry";
import { makeSignBytes, makeSignDoc } from "./signing";
@ -22,25 +23,23 @@ describe("signing", () => {
const prefixedPubkeyBytes = Uint8Array.from([0x0a, pubkeyBytes.length, ...pubkeyBytes]);
testVectors.forEach(({ outputs: { signedTxBytes } }) => {
const parsedTestTx = Tx.decode(fromHex(signedTxBytes));
const parsedTestTx = decodeTxRaw(fromHex(signedTxBytes));
expect(parsedTestTx.signatures.length).toEqual(1);
expect(parsedTestTx.authInfo!.signerInfos.length).toEqual(1);
expect(Uint8Array.from(parsedTestTx.authInfo!.signerInfos[0].publicKey!.value ?? [])).toEqual(
expect(parsedTestTx.authInfo.signerInfos.length).toEqual(1);
expect(Uint8Array.from(parsedTestTx.authInfo.signerInfos[0].publicKey!.value ?? [])).toEqual(
prefixedPubkeyBytes,
);
expect(parsedTestTx.authInfo?.signerInfos![0].modeInfo!.single!.mode).toEqual(
SignMode.SIGN_MODE_DIRECT,
);
expect({ ...parsedTestTx.authInfo!.fee!.amount[0] }).toEqual({ denom: "ucosm", amount: "2000" });
expect(parsedTestTx.authInfo!.fee!.gasLimit.toString()).toEqual(gasLimit.toString());
expect(parsedTestTx.body!.extensionOptions).toEqual([]);
expect(parsedTestTx.body!.nonCriticalExtensionOptions).toEqual([]);
expect(parsedTestTx.body!.messages.length).toEqual(1);
expect(parsedTestTx.authInfo.signerInfos[0].modeInfo!.single!.mode).toEqual(SignMode.SIGN_MODE_DIRECT);
expect({ ...parsedTestTx.authInfo.fee!.amount[0] }).toEqual({ denom: "ucosm", amount: "2000" });
expect(parsedTestTx.authInfo.fee!.gasLimit.toString()).toEqual(gasLimit.toString());
expect(parsedTestTx.body.extensionOptions).toEqual([]);
expect(parsedTestTx.body.nonCriticalExtensionOptions).toEqual([]);
expect(parsedTestTx.body.messages.length).toEqual(1);
const registry = new Registry();
const parsedTestTxMsg = registry.decode({
typeUrl: parsedTestTx.body!.messages[0].typeUrl,
value: parsedTestTx.body!.messages[0].value,
typeUrl: parsedTestTx.body.messages[0].typeUrl,
value: parsedTestTx.body.messages[0].value,
});
expect(parsedTestTxMsg.fromAddress).toEqual(address);
expect(parsedTestTxMsg.toAddress).toEqual(toAddress);

View File

@ -4,12 +4,13 @@ import { coin, coins, DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-si
import { assert, sleep } from "@cosmjs/utils";
import protobuf from "protobufjs/minimal";
import { decodeTxRaw } from "../../proto-signing/build";
import { AminoMsgDelegate } from "./aminomsgs";
import { AminoTypes } from "./aminotypes";
import { MsgSend } from "./codec/cosmos/bank/v1beta1/tx";
import { Coin } from "./codec/cosmos/base/v1beta1/coin";
import { DeepPartial, MsgDelegate } from "./codec/cosmos/staking/v1beta1/tx";
import { AuthInfo, Tx, TxBody, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { AuthInfo, TxBody, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { MsgDelegateEncodeObject, MsgSendEncodeObject } from "./encodeobjects";
import { GasPrice } from "./fee";
import { PrivateSigningStargateClient, SigningStargateClient } from "./signingstargateclient";
@ -371,11 +372,11 @@ describe("SigningStargateClient", () => {
const searchResult = await client.getTx(result.transactionHash);
assert(searchResult, "Must find transaction");
const tx = Tx.decode(searchResult.tx);
const tx = decodeTxRaw(searchResult.tx);
// From ModifyingDirectSecp256k1HdWallet
expect(tx.body!.memo).toEqual("This was modified");
expect({ ...tx.authInfo!.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo!.fee!.gasLimit.toNumber()).toEqual(333333);
expect(tx.body.memo).toEqual("This was modified");
expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
});
});
@ -568,11 +569,11 @@ describe("SigningStargateClient", () => {
const searchResult = await client.getTx(result.transactionHash);
assert(searchResult, "Must find transaction");
const tx = Tx.decode(searchResult.tx);
const tx = decodeTxRaw(searchResult.tx);
// From ModifyingSecp256k1HdWallet
expect(tx.body!.memo).toEqual("This was modified");
expect({ ...tx.authInfo!.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo!.fee!.gasLimit.toNumber()).toEqual(333333);
expect(tx.body.memo).toEqual("This was modified");
expect({ ...tx.authInfo.fee!.amount[0] }).toEqual(coin(3000, "ucosm"));
expect(tx.authInfo.fee!.gasLimit.toNumber()).toEqual(333333);
});
});
});

View File

@ -10,8 +10,9 @@ import {
} from "@cosmjs/proto-signing";
import { assert, sleep } from "@cosmjs/utils";
import { decodeTxRaw } from "../../proto-signing/build";
import { Coin } from "./codec/cosmos/base/v1beta1/coin";
import { Tx, TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { TxRaw } from "./codec/cosmos/tx/v1beta1/tx";
import { isMsgSendEncodeObject } from "./encodeobjects";
import {
BroadcastTxResponse,
@ -231,8 +232,8 @@ describe("StargateClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages.filter((msg) => {
const tx = decodeTxRaw(result.tx);
const filteredMsgs = tx.body.messages.filter((msg) => {
if (!isMsgSendEncodeObject(msg)) return false;
const decoded = registry.decode(msg);
return decoded.fromAddress === sendSuccessful?.sender;
@ -259,8 +260,8 @@ describe("StargateClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const filteredMsgs = tx.body!.messages.filter((msg) => {
const tx = decodeTxRaw(result.tx);
const filteredMsgs = tx.body.messages.filter((msg) => {
if (!isMsgSendEncodeObject(msg)) return false;
const decoded = registry.decode(msg);
return decoded.toAddress === sendSuccessful?.recipient;
@ -345,8 +346,8 @@ describe("StargateClient.getTx and .searchTx", () => {
// Check basic structure of all results
for (const result of results) {
const tx = Tx.decode(result.tx);
const msg = fromOneElementArray(tx.body!.messages);
const tx = decodeTxRaw(result.tx);
const msg = fromOneElementArray(tx.body.messages);
expect(msg.typeUrl).toEqual("/cosmos.bank.v1beta1.MsgSend");
const decoded = registry.decode(msg);
expect(decoded.toAddress).toEqual(sendSuccessful.recipient);

View File

@ -58,6 +58,20 @@ export interface IndexedTx {
/** Transaction execution error code. 0 on success. */
readonly code: number;
readonly rawLog: string;
/**
* Raw transaction bytes stored in Tendermint.
*
* If you hash this, you get the transaction hash (= transaction ID):
*
* ```js
* import { sha256 } from "@cosmjs/crypto";
* import { toHex } from "@cosmjs/encoding";
*
* const transactionId = toHex(sha256(indexTx.tx)).toUpperCase();
* ```
*
* Use `decodeTxRaw` from @cosmjs/proto-signing to decode this.
*/
readonly tx: Uint8Array;
readonly gasUsed: number;
readonly gasWanted: number;