Cleanup StdTx

This commit is contained in:
Simon Warta 2020-09-24 11:37:15 +02:00
parent 1b0eec8ed3
commit 0f626a2320
25 changed files with 137 additions and 78 deletions

View File

@ -55,6 +55,9 @@
`StdSignDoc`.
- @cosmjs/launchpad: Remove `makeSignBytes` in favour of `makeSignDoc` and
`serializeSignDoc`.
- @cosmjs/launchpad: Create `WrappedTx`, `WrappedStdTx` and `isWrappedStdTx` to
better represent the Amino tx interface. Deprecate `CosmosSdkTx`, which is an
alias for `WrappedStdTx`.
- @cosmjs/launchpad-ledger: Add package supporting Ledger device integration for
Launchpad. Two new classes are provided: `LedgerSigner` (for most use cases)
and `LaunchpadLedger` for more fine-grained access.

View File

@ -2,13 +2,13 @@
import {
Coin,
coins,
CosmosSdkTx,
isBroadcastTxFailure,
isMsgSend,
LcdClient,
makeSignDoc,
MsgSend,
Secp256k1Wallet,
WrappedStdTx,
} from "@cosmjs/launchpad";
import { assert, sleep } from "@cosmjs/utils";
@ -30,7 +30,7 @@ interface TestTxSend {
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
}
interface TestTxExecute {
@ -38,7 +38,7 @@ interface TestTxExecute {
readonly contract: string;
readonly hash: string;
readonly height: number;
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
}
describe("CosmWasmClient.searchTx", () => {
@ -105,7 +105,7 @@ describe("CosmWasmClient.searchTx", () => {
const chainId = await client.getChainId();
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signature } = await wallet.sign(alice.address0, signDoc);
const tx: CosmosSdkTx = {
const tx: WrappedStdTx = {
type: "cosmos-sdk/StdTx",
value: {
msg: [sendMsg],

View File

@ -3,6 +3,7 @@ import { Sha256 } from "@cosmjs/crypto";
import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@cosmjs/encoding";
import {
assertIsBroadcastTxSuccess,
isWrappedStdTx,
makeSignDoc,
MsgSend,
Secp256k1Wallet,
@ -200,6 +201,7 @@ describe("CosmWasmClient", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(wasmd.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
expect(await client.getIdentifier(cosmoshub.tx)).toEqual(cosmoshub.id);
});
});

View File

@ -5,7 +5,6 @@ import {
BroadcastMode,
BroadcastTxResult,
Coin,
CosmosSdkTx,
IndexedTx,
LcdClient,
normalizePubkey,
@ -13,6 +12,7 @@ import {
setupAuthExtension,
StdTx,
uint64ToNumber,
WrappedStdTx,
} from "@cosmjs/launchpad";
import { Uint53 } from "@cosmjs/math";
@ -199,7 +199,7 @@ export class CosmWasmClient {
/**
* Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID)
*/
public async getIdentifier(tx: CosmosSdkTx): Promise<string> {
public async getIdentifier(tx: WrappedStdTx): Promise<string> {
// We consult the REST API because we don't have a local amino encoder
const response = await this.lcdClient.encodeTx(tx);
const hash = new Sha256(fromBase64(response.tx)).digest();

View File

@ -3,11 +3,11 @@ import {
BroadcastMode,
BroadcastTxResult,
Coin,
CosmosSdkTx,
IndexedTx,
LcdClient,
PubKey,
StdTx,
WrappedStdTx,
} from "@cosmjs/launchpad";
import { WasmExtension } from "./lcdapi/wasm";
import { JsonObject } from "./types";
@ -132,7 +132,7 @@ export declare class CosmWasmClient {
/**
* Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID)
*/
getIdentifier(tx: CosmosSdkTx): Promise<string>;
getIdentifier(tx: WrappedStdTx): Promise<string>;
/**
* Returns account number and sequence.
*

View File

@ -16,14 +16,14 @@ import {
wasmd,
wasmdEnabled,
} from "./testutils.spec";
import { CosmosSdkTx } from "./types";
import { WrappedStdTx } from "./tx";
interface TestTxSend {
readonly sender: string;
readonly recipient: string;
readonly hash: string;
readonly height: number;
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
}
describe("CosmosClient.searchTx", () => {
@ -57,7 +57,7 @@ describe("CosmosClient.searchTx", () => {
const chainId = await client.getChainId();
const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence);
const { signature } = await wallet.sign(walletAddress, signDoc);
const tx: CosmosSdkTx = {
const tx: WrappedStdTx = {
type: "cosmos-sdk/StdTx",
value: {
msg: [sendMsg],

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { sleep } from "@cosmjs/utils";
import { assert, sleep } from "@cosmjs/utils";
import { ReadonlyDate } from "readonly-date";
import { assertIsBroadcastTxSuccess, CosmosClient, PrivateCosmosClient } from "./cosmosclient";
@ -16,7 +16,8 @@ import {
unused,
wasmd,
} from "./testutils.spec";
import { StdFee, StdTx } from "./types";
import { isWrappedStdTx, StdTx } from "./tx";
import { StdFee } from "./types";
const blockTime = 1_000; // ms
@ -190,6 +191,7 @@ describe("CosmosClient", () => {
it("works", async () => {
pendingWithoutWasmd();
const client = new CosmosClient(wasmd.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
expect(await client.getIdentifier(cosmoshub.tx)).toEqual(cosmoshub.id);
});
});

View File

@ -12,7 +12,8 @@ import {
uint64ToNumber,
} from "./lcdapi";
import { Log, parseLogs } from "./logs";
import { CosmosSdkTx, PubKey, StdTx } from "./types";
import { StdTx, WrappedStdTx } from "./tx";
import { PubKey } from "./types";
export interface GetSequenceResult {
readonly accountNumber: number;
@ -121,7 +122,7 @@ export interface IndexedTx {
readonly code: number;
readonly rawLog: string;
readonly logs: readonly Log[];
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gasWanted?: number;
/** The gas used by the execution */
@ -203,7 +204,7 @@ export class CosmosClient {
/**
* Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID)
*/
public async getIdentifier(tx: CosmosSdkTx): Promise<string> {
public async getIdentifier(tx: WrappedStdTx): Promise<string> {
// We consult the REST API because we don't have a local amino encoder
const response = await this.lcdClient.encodeTx(tx);
const hash = new Sha256(fromBase64(response.tx)).digest();

View File

@ -100,6 +100,7 @@ export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer";
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
export { isStdTx, isWrappedStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx";
export { pubkeyType, PubKey, StdFee, StdSignature } from "./types";
export { makeCosmoshubPath, executeKdf, KdfConfiguration } from "./wallet";
export { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet";

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { CosmosSdkTx } from "../types";
import { WrappedStdTx } from "../tx";
/**
* The mode used to send transaction
@ -109,7 +109,7 @@ export interface TxsResponse {
readonly code?: number;
readonly raw_log: string;
readonly logs?: unknown[];
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gas_wanted?: string;
/** The gas used by the execution */

View File

@ -20,7 +20,8 @@ import {
wasmd,
wasmdEnabled,
} from "../testutils.spec";
import { StdFee, StdTx } from "../types";
import { isWrappedStdTx, StdTx } from "../tx";
import { StdFee } from "../types";
import { makeCosmoshubPath } from "../wallet";
import { setupAuthExtension } from "./auth";
import { TxsResponse } from "./base";
@ -493,6 +494,7 @@ describe("LcdClient", () => {
it("works for cosmoshub example", async () => {
pendingWithoutWasmd();
const client = new LcdClient(wasmd.endpoint);
assert(isWrappedStdTx(cosmoshub.tx));
const response = await client.encodeTx(cosmoshub.tx);
expect(response).toEqual(
jasmine.objectContaining({

View File

@ -2,7 +2,7 @@
import { assert, isNonNullObject } from "@cosmjs/utils";
import axios, { AxiosError, AxiosInstance } from "axios";
import { CosmosSdkTx, StdTx } from "../types";
import { StdTx, WrappedStdTx } from "../tx";
import {
BlockResponse,
BroadcastMode,
@ -284,7 +284,7 @@ export class LcdClient {
}
/** returns the amino-encoding of the transaction performed by the server */
public async encodeTx(tx: CosmosSdkTx): Promise<EncodeTxResponse> {
public async encodeTx(tx: WrappedStdTx): Promise<EncodeTxResponse> {
const responseData = await this.post("/txs/encode", tx);
if (!responseData.tx) {
throw new Error("Unexpected response data format");

View File

@ -1,7 +1,10 @@
import { assert } from "@cosmjs/utils";
import { findSequenceForSignedTx } from "./sequence";
import response1 from "./testdata/txresponse1.json";
import response2 from "./testdata/txresponse2.json";
import response3 from "./testdata/txresponse3.json";
import { isWrappedStdTx } from "./tx";
// Those values must match ./testdata/txresponse*.json
const chainId = "testing";
@ -10,6 +13,10 @@ const accountNumber = 4;
describe("sequence", () => {
describe("findSequenceForSignedTx", () => {
it("works", async () => {
assert(isWrappedStdTx(response1.tx));
assert(isWrappedStdTx(response2.tx));
assert(isWrappedStdTx(response3.tx));
const current = 100; // what we get from GET /auth/accounts/{address}
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, current)).toEqual(10);
// We know response3.height > response1.height, so the sequence must be at least 10+1
@ -19,6 +26,10 @@ describe("sequence", () => {
});
it("returns undefined when sequence is not in range", async () => {
assert(isWrappedStdTx(response1.tx));
assert(isWrappedStdTx(response2.tx));
assert(isWrappedStdTx(response3.tx));
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 5)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 11)).toBeUndefined();
expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 50)).toBeUndefined();

View File

@ -2,7 +2,7 @@ import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto";
import { makeSignDoc, serializeSignDoc } from "./encoding";
import { decodeSignature } from "./signature";
import { CosmosSdkTx } from "./types";
import { WrappedStdTx } from "./tx";
/**
* Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction
@ -16,7 +16,7 @@ import { CosmosSdkTx } from "./types";
* @returns the sequence if a match was found and undefined otherwise
*/
export async function findSequenceForSignedTx(
tx: CosmosSdkTx,
tx: WrappedStdTx,
chainId: string,
accountNumber: number,
upperBound: number,

View File

@ -6,7 +6,8 @@ import { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas";
import { BroadcastMode } from "./lcdapi";
import { Msg, MsgSend } from "./msgs";
import { OfflineSigner } from "./signer";
import { StdFee, StdTx } from "./types";
import { StdTx } from "./tx";
import { StdFee } from "./types";
/**
* These fees are used by the higher level methods of SigningCosmosClient

View File

@ -2,7 +2,8 @@ import { Random } from "@cosmjs/crypto";
import { Bech32 } from "@cosmjs/encoding";
import { Msg } from "./msgs";
import { StdFee, StdSignature, StdTx } from "./types";
import { StdTx } from "./tx";
import { StdFee, StdSignature } from "./types";
export function makeRandomAddress(): string {
return Bech32.encode("cosmos", Random.getBytes(20));

View File

@ -0,0 +1,44 @@
import { Msg } from "./msgs";
import { StdFee, StdSignature } from "./types";
/**
* A Cosmos SDK StdTx
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx
*/
export interface StdTx {
readonly msg: readonly Msg[];
readonly fee: StdFee;
readonly signatures: readonly StdSignature[];
readonly memo: string | undefined;
}
export function isStdTx(txValue: unknown): txValue is StdTx {
const { memo, msg, fee, signatures } = txValue as StdTx;
return (
typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures)
);
}
/**
* An Amino JSON wrapper around the Tx interface
*/
export interface WrappedTx {
readonly type: string;
readonly value: any;
}
/**
* An Amino JSON wrapper around StdTx
*/
export interface WrappedStdTx extends WrappedTx {
readonly type: "cosmos-sdk/StdTx";
readonly value: StdTx;
}
export function isWrappedStdTx(wrapped: WrappedTx): wrapped is WrappedStdTx {
return (wrapped as WrappedStdTx).type === "cosmos-sdk/StdTx" && isStdTx(wrapped.value);
}
/** @deprecated use WrappedStdTx */
export type CosmosSdkTx = WrappedStdTx;

View File

@ -1,30 +1,5 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { Coin } from "./coins";
import { Msg } from "./msgs";
/**
* A Cosmos SDK StdTx
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx
*/
export interface StdTx {
readonly msg: readonly Msg[];
readonly fee: StdFee;
readonly signatures: readonly StdSignature[];
readonly memo: string | undefined;
}
export function isStdTx(txValue: unknown): txValue is StdTx {
const { memo, msg, fee, signatures } = txValue as StdTx;
return (
typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures)
);
}
export interface CosmosSdkTx {
readonly type: string;
readonly value: StdTx;
}
export interface StdFee {
readonly amount: readonly Coin[];

View File

@ -1,7 +1,8 @@
import { Coin } from "./coins";
import { AuthExtension, BroadcastMode, LcdClient } from "./lcdapi";
import { Log } from "./logs";
import { CosmosSdkTx, PubKey, StdTx } from "./types";
import { StdTx, WrappedStdTx } from "./tx";
import { PubKey } from "./types";
export interface GetSequenceResult {
readonly accountNumber: number;
readonly sequence: number;
@ -78,7 +79,7 @@ export interface IndexedTx {
readonly code: number;
readonly rawLog: string;
readonly logs: readonly Log[];
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gasWanted?: number;
/** The gas used by the execution */
@ -127,7 +128,7 @@ export declare class CosmosClient {
/**
* Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID)
*/
getIdentifier(tx: CosmosSdkTx): Promise<string>;
getIdentifier(tx: WrappedStdTx): Promise<string>;
/**
* Returns account number and sequence.
*

View File

@ -98,6 +98,7 @@ export { findSequenceForSignedTx } from "./sequence";
export { encodeSecp256k1Signature, decodeSignature } from "./signature";
export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer";
export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient";
export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types";
export { isStdTx, isWrappedStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx";
export { pubkeyType, PubKey, StdFee, StdSignature } from "./types";
export { makeCosmoshubPath, executeKdf, KdfConfiguration } from "./wallet";
export { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet";

View File

@ -1,4 +1,4 @@
import { CosmosSdkTx } from "../types";
import { WrappedStdTx } from "../tx";
/**
* The mode used to send transaction
*
@ -93,7 +93,7 @@ export interface TxsResponse {
readonly code?: number;
readonly raw_log: string;
readonly logs?: unknown[];
readonly tx: CosmosSdkTx;
readonly tx: WrappedStdTx;
/** The gas limit as set by the user */
readonly gas_wanted?: string;
/** The gas used by the execution */

View File

@ -1,4 +1,4 @@
import { CosmosSdkTx, StdTx } from "../types";
import { StdTx, WrappedStdTx } from "../tx";
import {
BlockResponse,
BroadcastMode,
@ -151,7 +151,7 @@ export declare class LcdClient {
txById(id: string): Promise<TxsResponse>;
txsQuery(query: string): Promise<SearchTxsResponse>;
/** returns the amino-encoding of the transaction performed by the server */
encodeTx(tx: CosmosSdkTx): Promise<EncodeTxResponse>;
encodeTx(tx: WrappedStdTx): Promise<EncodeTxResponse>;
/**
* Broadcasts a signed transaction to the transaction pool.
* Depending on the client's broadcast mode, this might or might

View File

@ -1,4 +1,4 @@
import { CosmosSdkTx } from "./types";
import { WrappedStdTx } from "./tx";
/**
* Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction
*
@ -11,7 +11,7 @@ import { CosmosSdkTx } from "./types";
* @returns the sequence if a match was found and undefined otherwise
*/
export declare function findSequenceForSignedTx(
tx: CosmosSdkTx,
tx: WrappedStdTx,
chainId: string,
accountNumber: number,
upperBound: number,

31
packages/launchpad/types/tx.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
import { Msg } from "./msgs";
import { StdFee, StdSignature } from "./types";
/**
* A Cosmos SDK StdTx
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx
*/
export interface StdTx {
readonly msg: readonly Msg[];
readonly fee: StdFee;
readonly signatures: readonly StdSignature[];
readonly memo: string | undefined;
}
export declare function isStdTx(txValue: unknown): txValue is StdTx;
/**
* An Amino JSON wrapper around the Tx interface
*/
export interface WrappedTx {
readonly type: string;
readonly value: any;
}
/**
* An Amino JSON wrapper around StdTx
*/
export interface WrappedStdTx extends WrappedTx {
readonly type: "cosmos-sdk/StdTx";
readonly value: StdTx;
}
export declare function isWrappedStdTx(wrapped: WrappedTx): wrapped is WrappedStdTx;
/** @deprecated use WrappedStdTx */
export declare type CosmosSdkTx = WrappedStdTx;

View File

@ -1,21 +1,4 @@
import { Coin } from "./coins";
import { Msg } from "./msgs";
/**
* A Cosmos SDK StdTx
*
* @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx
*/
export interface StdTx {
readonly msg: readonly Msg[];
readonly fee: StdFee;
readonly signatures: readonly StdSignature[];
readonly memo: string | undefined;
}
export declare function isStdTx(txValue: unknown): txValue is StdTx;
export interface CosmosSdkTx {
readonly type: string;
readonly value: StdTx;
}
export interface StdFee {
readonly amount: readonly Coin[];
readonly gas: string;