From 34d19e4d7e25cd2827864a7515f51259c0d19625 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 9 Jun 2020 16:46:10 +0100 Subject: [PATCH 1/4] Reorganise error handling in sdk38 --- .../sdk38/src/cosmosclient.searchtx.spec.ts | 14 ++---- packages/sdk38/src/cosmosclient.spec.ts | 8 +++- packages/sdk38/src/cosmosclient.ts | 43 +++++++++++++------ packages/sdk38/src/restclient.spec.ts | 14 ++---- .../sdk38/src/signingcosmosclient.spec.ts | 5 ++- packages/sdk38/types/cosmosclient.d.ts | 14 +++++- 6 files changed, 61 insertions(+), 37 deletions(-) diff --git a/packages/sdk38/src/cosmosclient.searchtx.spec.ts b/packages/sdk38/src/cosmosclient.searchtx.spec.ts index c75c5e88..8f32d20b 100644 --- a/packages/sdk38/src/cosmosclient.searchtx.spec.ts +++ b/packages/sdk38/src/cosmosclient.searchtx.spec.ts @@ -3,7 +3,7 @@ import { Uint53 } from "@cosmjs/math"; import { assert, sleep } from "@cosmjs/utils"; import { Coin } from "./coins"; -import { CosmosClient } from "./cosmosclient"; +import { CosmosClient, isPostTxFailureResult } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { Secp256k1Pen } from "./pen"; import { RestClient } from "./restclient"; @@ -105,19 +105,13 @@ describe("CosmosClient.searchTx", () => { }, }; const transactionId = await client.getIdentifier(tx); - try { - await client.postTx(tx.value); - } catch (error) { - // postTx() throws on execution failures, which is a questionable design. Ignore for now. - // console.log(error); - const errorMessage: string = error.toString(); - const [_, heightMatch] = errorMessage.match(/at height ([0-9]+)/) || ["", ""]; - + const result = await client.postTx(tx.value); + if (isPostTxFailureResult(result)) { sendUnsuccessful = { sender: faucet.address, recipient: recipient, hash: transactionId, - height: Uint53.fromString(heightMatch).toNumber(), + height: Uint53.fromString(result.height).toNumber(), tx: tx, }; } diff --git a/packages/sdk38/src/cosmosclient.spec.ts b/packages/sdk38/src/cosmosclient.spec.ts index 3f777899..d64b5880 100644 --- a/packages/sdk38/src/cosmosclient.spec.ts +++ b/packages/sdk38/src/cosmosclient.spec.ts @@ -2,7 +2,7 @@ import { sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; -import { CosmosClient, PrivateCosmWasmClient } from "./cosmosclient"; +import { CosmosClient, isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { findAttribute } from "./logs"; import { Secp256k1Pen } from "./pen"; @@ -230,7 +230,11 @@ describe("CosmosClient", () => { memo: memo, signatures: [signature], }; - const { logs, transactionHash } = await client.postTx(signedTx); + const txResult = await client.postTx(signedTx); + if (isPostTxFailureResult(txResult)) { + throw new Error("Post tx failed"); + } + const { logs, transactionHash } = txResult; const amountAttr = findAttribute(logs, "transfer", "amount"); expect(amountAttr.value).toEqual("1234567ucosm"); expect(transactionHash).toMatch(/^[0-9A-F]{64}$/); diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 4c42f385..1da17f74 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -1,5 +1,5 @@ import { Sha256 } from "@cosmjs/crypto"; -import { fromBase64, toHex } from "@cosmjs/encoding"; +import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Coin } from "./coins"; import { Log, parseLogs } from "./logs"; @@ -21,11 +21,26 @@ export interface Account { readonly sequence: number; } -export interface PostTxResult { +export interface PostTxFailureResult { + /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ + readonly transactionHash: string; + readonly height: string; + readonly code: number; + readonly rawLog: string; +} + +export interface PostTxSuccessResult { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; + readonly data?: Uint8Array; +} + +export type PostTxResult = PostTxSuccessResult | PostTxFailureResult; + +export function isPostTxFailureResult(postTxResult: PostTxResult): postTxResult is PostTxFailureResult { + return (postTxResult as PostTxFailureResult).code !== undefined; } export interface SearchByIdQuery { @@ -276,17 +291,19 @@ export class CosmosClient { throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex"); } - if (result.code) { - throw new Error( - `Error when posting tx ${result.txhash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.raw_log}`, - ); - } - - return { - logs: result.logs ? parseLogs(result.logs) : [], - rawLog: result.raw_log || "", - transactionHash: result.txhash, - }; + return result.code !== undefined + ? { + height: result.height, + transactionHash: result.txhash, + code: result.code, + rawLog: result.raw_log || "", + } + : { + logs: result.logs ? parseLogs(result.logs) : [], + rawLog: result.raw_log || "", + transactionHash: result.txhash, + data: result.data ? fromHex(result.data) : undefined, + }; } private async txsQuery(query: string): Promise { diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index 92d3f0ed..478d6ed2 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -4,6 +4,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; import { rawSecp256k1PubkeyToAddress } from "./address"; +import { isPostTxFailureResult } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { parseLogs } from "./logs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; @@ -275,11 +276,9 @@ describe("RestClient", () => { signatures: [signature], }; const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); - try { - await client.postTx(signedTx); - } catch (error) { - // postTx() throws on execution failures, which is a questionable design. Ignore for now. - // console.log(error); + const result = await client.postTx(signedTx); + if (!isPostTxFailureResult(result)) { + throw new Error("Post tx succeeded unexpectedly"); } unsuccessful = { sender: faucet.address, @@ -631,7 +630,6 @@ describe("RestClient", () => { signatures: [signature1, signature2, signature3], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(4); expect(postResult.raw_log).toContain("wrong number of signers"); }); @@ -691,7 +689,6 @@ describe("RestClient", () => { signatures: [signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toBeUndefined(); }); @@ -755,7 +752,6 @@ describe("RestClient", () => { signatures: [signature2, signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toBeUndefined(); await sleep(500); @@ -824,7 +820,6 @@ describe("RestClient", () => { signatures: [signature2, signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(8); }); @@ -888,7 +883,6 @@ describe("RestClient", () => { signatures: [signature1, signature2], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(8); }); }); diff --git a/packages/sdk38/src/signingcosmosclient.spec.ts b/packages/sdk38/src/signingcosmosclient.spec.ts index f2c186ba..cc9600e2 100644 --- a/packages/sdk38/src/signingcosmosclient.spec.ts +++ b/packages/sdk38/src/signingcosmosclient.spec.ts @@ -1,7 +1,7 @@ import { assert } from "@cosmjs/utils"; import { Coin } from "./coins"; -import { PrivateCosmWasmClient } from "./cosmosclient"; +import { isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmosclient"; import { Secp256k1Pen } from "./pen"; import { SigningCosmosClient } from "./signingcosmosclient"; import { makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; @@ -66,6 +66,9 @@ describe("SigningCosmosClient", () => { // send const result = await client.sendTokens(beneficiaryAddress, transferAmount, "for dinner"); + if (isPostTxFailureResult(result)) { + throw new Error("Send tokens failed"); + } const [firstLog] = result.logs; expect(firstLog).toBeTruthy(); diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index 0af6120a..db2cf20b 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -14,12 +14,24 @@ export interface Account { readonly accountNumber: number; readonly sequence: number; } -export interface PostTxResult { +export interface PostTxFailureResult { + /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ + readonly transactionHash: string; + readonly height: string; + readonly code: number; + readonly rawLog: string; +} +export interface PostTxSuccessResult { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; + readonly data?: Uint8Array; } +export declare type PostTxResult = PostTxSuccessResult | PostTxFailureResult; +export declare function isPostTxFailureResult( + postTxResult: PostTxResult, +): postTxResult is PostTxFailureResult; export interface SearchByIdQuery { readonly id: string; } From 84cf7192294341025fcc94075c0a4b94c576c64a Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 9 Jun 2020 16:59:24 +0100 Subject: [PATCH 2/4] Reorganise error handling in cosmwasm --- .../src/cosmwasmclient.searchtx.spec.ts | 14 ++----- packages/cosmwasm/src/cosmwasmclient.spec.ts | 8 +++- packages/cosmwasm/src/cosmwasmclient.ts | 41 +++++++++++++------ packages/cosmwasm/src/restclient.spec.ts | 14 ++----- .../src/signingcosmwasmclient.spec.ts | 5 ++- .../cosmwasm/src/signingcosmwasmclient.ts | 22 +++++++++- packages/cosmwasm/types/cosmwasmclient.d.ts | 14 ++++++- 7 files changed, 81 insertions(+), 37 deletions(-) diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index f6bef50d..66e3d76d 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -3,7 +3,7 @@ import { Uint53 } from "@cosmjs/math"; import { Coin, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; -import { CosmWasmClient } from "./cosmwasmclient"; +import { CosmWasmClient, isPostTxFailureResult } from "./cosmwasmclient"; import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs"; import { RestClient } from "./restclient"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; @@ -113,19 +113,13 @@ describe("CosmWasmClient.searchTx", () => { }, }; const transactionId = await client.getIdentifier(tx); - try { - await client.postTx(tx.value); - } catch (error) { - // postTx() throws on execution failures, which is a questionable design. Ignore for now. - // console.log(error); - const errorMessage: string = error.toString(); - const [_, heightMatch] = errorMessage.match(/at height ([0-9]+)/) || ["", ""]; - + const result = await client.postTx(tx.value); + if (isPostTxFailureResult(result)) { sendUnsuccessful = { sender: alice.address0, recipient: recipient, hash: transactionId, - height: Uint53.fromString(heightMatch).toNumber(), + height: Uint53.fromString(result.height).toNumber(), tx: tx, }; } diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index b28fe430..9cea0772 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -5,7 +5,7 @@ import { makeSignBytes, MsgSend, Secp256k1Pen, StdFee } from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; -import { Code, CosmWasmClient, PrivateCosmWasmClient } from "./cosmwasmclient"; +import { Code, CosmWasmClient, isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmwasmclient"; import { findAttribute } from "./logs"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; import cosmoshub from "./testdata/cosmoshub.json"; @@ -242,7 +242,11 @@ describe("CosmWasmClient", () => { memo: memo, signatures: [signature], }; - const { logs, transactionHash } = await client.postTx(signedTx); + const result = await client.postTx(signedTx); + if (isPostTxFailureResult(result)) { + throw new Error("Post tx failed"); + } + const { logs, transactionHash } = result; const amountAttr = findAttribute(logs, "transfer", "amount"); expect(amountAttr.value).toEqual("1234567ucosm"); expect(transactionHash).toMatch(/^[0-9A-F]{64}$/); diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index 886b860a..f7d16715 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -28,11 +28,26 @@ export interface Account { readonly sequence: number; } -export interface PostTxResult { +export interface PostTxFailureResult { + /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ + readonly transactionHash: string; + readonly height: string; + readonly code: number; + readonly rawLog: string; +} + +export interface PostTxSuccessResult { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; + readonly data?: Uint8Array; +} + +export type PostTxResult = PostTxSuccessResult | PostTxFailureResult; + +export function isPostTxFailureResult(postTxResult: PostTxResult): postTxResult is PostTxFailureResult { + return (postTxResult as PostTxFailureResult).code !== undefined; } export interface SearchByIdQuery { @@ -294,17 +309,19 @@ export class CosmWasmClient { throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex"); } - if (result.code) { - throw new Error( - `Error when posting tx ${result.txhash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.raw_log}`, - ); - } - - return { - logs: result.logs ? parseLogs(result.logs) : [], - rawLog: result.raw_log || "", - transactionHash: result.txhash, - }; + return result.code !== undefined + ? { + height: result.height, + transactionHash: result.txhash, + code: result.code, + rawLog: result.raw_log || "", + } + : { + logs: result.logs ? parseLogs(result.logs) : [], + rawLog: result.raw_log || "", + transactionHash: result.txhash, + data: result.data ? fromHex(result.data) : undefined, + }; } public async getCodes(): Promise { diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index 007291b1..16a64076 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -19,6 +19,7 @@ import { import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; +import { isPostTxFailureResult } from "./cosmwasmclient"; import { findAttribute, parseLogs } from "./logs"; import { isMsgInstantiateContract, @@ -400,11 +401,9 @@ describe("RestClient", () => { signatures: [signature], }; const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); - try { - await client.postTx(signedTx); - } catch (error) { - // postTx() throws on execution failures, which is a questionable design. Ignore for now. - // console.log(error); + const result = await client.postTx(signedTx); + if (!isPostTxFailureResult(result)) { + throw new Error("Post tx succeeded unexpectedly"); } unsuccessful = { sender: alice.address0, @@ -858,7 +857,6 @@ describe("RestClient", () => { signatures: [signature1, signature2, signature3], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(4); expect(postResult.raw_log).toContain("wrong number of signers"); }); @@ -918,7 +916,6 @@ describe("RestClient", () => { signatures: [signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toBeUndefined(); }); @@ -982,7 +979,6 @@ describe("RestClient", () => { signatures: [signature2, signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toBeUndefined(); await sleep(500); @@ -1051,7 +1047,6 @@ describe("RestClient", () => { signatures: [signature2, signature1], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(8); }); @@ -1115,7 +1110,6 @@ describe("RestClient", () => { signatures: [signature1, signature2], }; const postResult = await client.postTx(signedTx); - // console.log(postResult.raw_log); expect(postResult.code).toEqual(8); }); diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index aab2c4b2..ad8d9330 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -3,7 +3,7 @@ import { toHex } from "@cosmjs/encoding"; import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; -import { PrivateCosmWasmClient } from "./cosmwasmclient"; +import { isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmwasmclient"; import { RestClient } from "./restclient"; import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient"; import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; @@ -205,6 +205,9 @@ describe("SigningCosmWasmClient", () => { // send const result = await client.sendTokens(beneficiaryAddress, transferAmount, "for dinner"); + if (isPostTxFailureResult(result)) { + throw new Error("Send tokens failed"); + } const [firstLog] = result.logs; expect(firstLog).toBeTruthy(); diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index 7dd234fe..92690c19 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -4,7 +4,14 @@ import { BroadcastMode, Coin, coins, makeSignBytes, MsgSend, StdFee, StdSignatur import pako from "pako"; import { isValidBuilder } from "./builder"; -import { Account, CosmWasmClient, GetNonceResult, PostTxResult } from "./cosmwasmclient"; +import { + Account, + CosmWasmClient, + GetNonceResult, + isPostTxFailureResult, + PostTxFailureResult, + PostTxResult, +} from "./cosmwasmclient"; import { findAttribute, Log } from "./logs"; import { MsgExecuteContract, MsgInstantiateContract, MsgStoreCode } from "./msgs"; @@ -84,6 +91,10 @@ export interface ExecuteResult { readonly transactionHash: string; } +function createPostTxErrorMessage(result: PostTxFailureResult): string { + return `Error when posting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`; +} + export class SigningCosmWasmClient extends CosmWasmClient { public readonly senderAddress: string; @@ -154,6 +165,9 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); + if (isPostTxFailureResult(result)) { + throw new Error(createPostTxErrorMessage(result)); + } const codeIdAttr = findAttribute(result.logs, "message", "code_id"); return { originalSize: wasmCode.length, @@ -200,6 +214,9 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); + if (isPostTxFailureResult(result)) { + throw new Error(createPostTxErrorMessage(result)); + } const contractAddressAttr = findAttribute(result.logs, "message", "contract_address"); return { contractAddress: contractAddressAttr.value, @@ -237,6 +254,9 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); + if (isPostTxFailureResult(result)) { + throw new Error(createPostTxErrorMessage(result)); + } return { logs: result.logs, transactionHash: result.transactionHash, diff --git a/packages/cosmwasm/types/cosmwasmclient.d.ts b/packages/cosmwasm/types/cosmwasmclient.d.ts index f423bae4..6ce66de0 100644 --- a/packages/cosmwasm/types/cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cosmwasmclient.d.ts @@ -14,12 +14,24 @@ export interface Account { readonly accountNumber: number; readonly sequence: number; } -export interface PostTxResult { +export interface PostTxFailureResult { + /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ + readonly transactionHash: string; + readonly height: string; + readonly code: number; + readonly rawLog: string; +} +export interface PostTxSuccessResult { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; + readonly data?: Uint8Array; } +export declare type PostTxResult = PostTxSuccessResult | PostTxFailureResult; +export declare function isPostTxFailureResult( + postTxResult: PostTxResult, +): postTxResult is PostTxFailureResult; export interface SearchByIdQuery { readonly id: string; } From b121eabca4b17672d457e3b25361ee919a684354 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 10 Jun 2020 10:42:05 +0100 Subject: [PATCH 3/4] Update sdk38 error handling for review comments --- packages/sdk38/src/cosmosclient.searchtx.spec.ts | 7 +++---- packages/sdk38/src/cosmosclient.spec.ts | 8 +++----- packages/sdk38/src/cosmosclient.ts | 15 ++++++++------- packages/sdk38/src/restclient.spec.ts | 6 ++---- packages/sdk38/src/signingcosmosclient.spec.ts | 6 ++---- packages/sdk38/types/cosmosclient.d.ts | 12 +++++------- 6 files changed, 23 insertions(+), 31 deletions(-) diff --git a/packages/sdk38/src/cosmosclient.searchtx.spec.ts b/packages/sdk38/src/cosmosclient.searchtx.spec.ts index 8f32d20b..b1c75d60 100644 --- a/packages/sdk38/src/cosmosclient.searchtx.spec.ts +++ b/packages/sdk38/src/cosmosclient.searchtx.spec.ts @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { Uint53 } from "@cosmjs/math"; import { assert, sleep } from "@cosmjs/utils"; import { Coin } from "./coins"; -import { CosmosClient, isPostTxFailureResult } from "./cosmosclient"; +import { CosmosClient, isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { Secp256k1Pen } from "./pen"; import { RestClient } from "./restclient"; @@ -106,12 +105,12 @@ describe("CosmosClient.searchTx", () => { }; const transactionId = await client.getIdentifier(tx); const result = await client.postTx(tx.value); - if (isPostTxFailureResult(result)) { + if (isPostTxFailure(result)) { sendUnsuccessful = { sender: faucet.address, recipient: recipient, hash: transactionId, - height: Uint53.fromString(result.height).toNumber(), + height: result.height, tx: tx, }; } diff --git a/packages/sdk38/src/cosmosclient.spec.ts b/packages/sdk38/src/cosmosclient.spec.ts index d64b5880..47433ee9 100644 --- a/packages/sdk38/src/cosmosclient.spec.ts +++ b/packages/sdk38/src/cosmosclient.spec.ts @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { sleep } from "@cosmjs/utils"; +import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; -import { CosmosClient, isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmosclient"; +import { CosmosClient, isPostTxFailure, PrivateCosmWasmClient } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { findAttribute } from "./logs"; import { Secp256k1Pen } from "./pen"; @@ -231,9 +231,7 @@ describe("CosmosClient", () => { signatures: [signature], }; const txResult = await client.postTx(signedTx); - if (isPostTxFailureResult(txResult)) { - throw new Error("Post tx failed"); - } + assert(!isPostTxFailure(txResult)); const { logs, transactionHash } = txResult; const amountAttr = findAttribute(logs, "transfer", "amount"); expect(amountAttr.value).toEqual("1234567ucosm"); diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 1da17f74..5b43dc97 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -1,5 +1,6 @@ import { Sha256 } from "@cosmjs/crypto"; import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; +import { Uint53 } from "@cosmjs/math"; import { Coin } from "./coins"; import { Log, parseLogs } from "./logs"; @@ -21,15 +22,15 @@ export interface Account { readonly sequence: number; } -export interface PostTxFailureResult { +export interface PostTxFailure { /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; - readonly height: string; + readonly height: number; readonly code: number; readonly rawLog: string; } -export interface PostTxSuccessResult { +export interface PostTxSuccess { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ @@ -37,10 +38,10 @@ export interface PostTxSuccessResult { readonly data?: Uint8Array; } -export type PostTxResult = PostTxSuccessResult | PostTxFailureResult; +export type PostTxResult = PostTxSuccess | PostTxFailure; -export function isPostTxFailureResult(postTxResult: PostTxResult): postTxResult is PostTxFailureResult { - return (postTxResult as PostTxFailureResult).code !== undefined; +export function isPostTxFailure(postTxResult: PostTxResult): postTxResult is PostTxFailure { + return !!(postTxResult as PostTxFailure).code; } export interface SearchByIdQuery { @@ -293,7 +294,7 @@ export class CosmosClient { return result.code !== undefined ? { - height: result.height, + height: Uint53.fromString(result.height).toNumber(), transactionHash: result.txhash, code: result.code, rawLog: result.raw_log || "", diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index 478d6ed2..bdaea11a 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -4,7 +4,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; import { rawSecp256k1PubkeyToAddress } from "./address"; -import { isPostTxFailureResult } from "./cosmosclient"; +import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { parseLogs } from "./logs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; @@ -277,9 +277,7 @@ describe("RestClient", () => { }; const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); const result = await client.postTx(signedTx); - if (!isPostTxFailureResult(result)) { - throw new Error("Post tx succeeded unexpectedly"); - } + assert(isPostTxFailure(result)); unsuccessful = { sender: faucet.address, recipient: recipient, diff --git a/packages/sdk38/src/signingcosmosclient.spec.ts b/packages/sdk38/src/signingcosmosclient.spec.ts index cc9600e2..258c4f76 100644 --- a/packages/sdk38/src/signingcosmosclient.spec.ts +++ b/packages/sdk38/src/signingcosmosclient.spec.ts @@ -1,7 +1,7 @@ import { assert } from "@cosmjs/utils"; import { Coin } from "./coins"; -import { isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmosclient"; +import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmosclient"; import { Secp256k1Pen } from "./pen"; import { SigningCosmosClient } from "./signingcosmosclient"; import { makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; @@ -66,9 +66,7 @@ describe("SigningCosmosClient", () => { // send const result = await client.sendTokens(beneficiaryAddress, transferAmount, "for dinner"); - if (isPostTxFailureResult(result)) { - throw new Error("Send tokens failed"); - } + assert(!isPostTxFailure(result)); const [firstLog] = result.logs; expect(firstLog).toBeTruthy(); diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index db2cf20b..4749f6b9 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -14,24 +14,22 @@ export interface Account { readonly accountNumber: number; readonly sequence: number; } -export interface PostTxFailureResult { +export interface PostTxFailure { /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; - readonly height: string; + readonly height: number; readonly code: number; readonly rawLog: string; } -export interface PostTxSuccessResult { +export interface PostTxSuccess { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; readonly data?: Uint8Array; } -export declare type PostTxResult = PostTxSuccessResult | PostTxFailureResult; -export declare function isPostTxFailureResult( - postTxResult: PostTxResult, -): postTxResult is PostTxFailureResult; +export declare type PostTxResult = PostTxSuccess | PostTxFailure; +export declare function isPostTxFailure(postTxResult: PostTxResult): postTxResult is PostTxFailure; export interface SearchByIdQuery { readonly id: string; } From 8c443dd134b7a0f9f44765389318a4b70badb418 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Wed, 10 Jun 2020 10:42:30 +0100 Subject: [PATCH 4/4] Update cosmwasm error handling for review comments --- .../cosmwasm/src/cosmwasmclient.searchtx.spec.ts | 7 +++---- packages/cosmwasm/src/cosmwasmclient.spec.ts | 6 ++---- packages/cosmwasm/src/cosmwasmclient.ts | 15 ++++++++------- packages/cosmwasm/src/restclient.spec.ts | 6 ++---- .../cosmwasm/src/signingcosmwasmclient.spec.ts | 6 ++---- packages/cosmwasm/src/signingcosmwasmclient.ts | 12 ++++++------ packages/cosmwasm/types/cosmwasmclient.d.ts | 12 +++++------- 7 files changed, 28 insertions(+), 36 deletions(-) diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index 66e3d76d..9ed9a979 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { Uint53 } from "@cosmjs/math"; import { Coin, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; -import { CosmWasmClient, isPostTxFailureResult } from "./cosmwasmclient"; +import { CosmWasmClient, isPostTxFailure } from "./cosmwasmclient"; import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs"; import { RestClient } from "./restclient"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; @@ -114,12 +113,12 @@ describe("CosmWasmClient.searchTx", () => { }; const transactionId = await client.getIdentifier(tx); const result = await client.postTx(tx.value); - if (isPostTxFailureResult(result)) { + if (isPostTxFailure(result)) { sendUnsuccessful = { sender: alice.address0, recipient: recipient, hash: transactionId, - height: Uint53.fromString(result.height).toNumber(), + height: result.height, tx: tx, }; } diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 9cea0772..1898982e 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -5,7 +5,7 @@ import { makeSignBytes, MsgSend, Secp256k1Pen, StdFee } from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; -import { Code, CosmWasmClient, isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmwasmclient"; +import { Code, CosmWasmClient, isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient"; import { findAttribute } from "./logs"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; import cosmoshub from "./testdata/cosmoshub.json"; @@ -243,9 +243,7 @@ describe("CosmWasmClient", () => { signatures: [signature], }; const result = await client.postTx(signedTx); - if (isPostTxFailureResult(result)) { - throw new Error("Post tx failed"); - } + assert(!isPostTxFailure(result)); const { logs, transactionHash } = result; const amountAttr = findAttribute(logs, "transfer", "amount"); expect(amountAttr.value).toEqual("1234567ucosm"); diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index f7d16715..45f88de7 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -1,5 +1,6 @@ import { Sha256 } from "@cosmjs/crypto"; import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; +import { Uint53 } from "@cosmjs/math"; import { BroadcastMode, Coin, @@ -28,15 +29,15 @@ export interface Account { readonly sequence: number; } -export interface PostTxFailureResult { +export interface PostTxFailure { /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; - readonly height: string; + readonly height: number; readonly code: number; readonly rawLog: string; } -export interface PostTxSuccessResult { +export interface PostTxSuccess { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ @@ -44,10 +45,10 @@ export interface PostTxSuccessResult { readonly data?: Uint8Array; } -export type PostTxResult = PostTxSuccessResult | PostTxFailureResult; +export type PostTxResult = PostTxSuccess | PostTxFailure; -export function isPostTxFailureResult(postTxResult: PostTxResult): postTxResult is PostTxFailureResult { - return (postTxResult as PostTxFailureResult).code !== undefined; +export function isPostTxFailure(postTxResult: PostTxResult): postTxResult is PostTxFailure { + return !!(postTxResult as PostTxFailure).code; } export interface SearchByIdQuery { @@ -311,7 +312,7 @@ export class CosmWasmClient { return result.code !== undefined ? { - height: result.height, + height: Uint53.fromString(result.height).toNumber(), transactionHash: result.txhash, code: result.code, rawLog: result.raw_log || "", diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index 16a64076..39a2a765 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -19,7 +19,7 @@ import { import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; -import { isPostTxFailureResult } from "./cosmwasmclient"; +import { isPostTxFailure } from "./cosmwasmclient"; import { findAttribute, parseLogs } from "./logs"; import { isMsgInstantiateContract, @@ -402,9 +402,7 @@ describe("RestClient", () => { }; const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); const result = await client.postTx(signedTx); - if (!isPostTxFailureResult(result)) { - throw new Error("Post tx succeeded unexpectedly"); - } + assert(isPostTxFailure(result)); unsuccessful = { sender: alice.address0, recipient: recipient, diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index ad8d9330..92a1489a 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -3,7 +3,7 @@ import { toHex } from "@cosmjs/encoding"; import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; -import { isPostTxFailureResult, PrivateCosmWasmClient } from "./cosmwasmclient"; +import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient"; import { RestClient } from "./restclient"; import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient"; import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; @@ -205,9 +205,7 @@ describe("SigningCosmWasmClient", () => { // send const result = await client.sendTokens(beneficiaryAddress, transferAmount, "for dinner"); - if (isPostTxFailureResult(result)) { - throw new Error("Send tokens failed"); - } + assert(!isPostTxFailure(result)); const [firstLog] = result.logs; expect(firstLog).toBeTruthy(); diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index 92690c19..3e52b236 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -8,8 +8,8 @@ import { Account, CosmWasmClient, GetNonceResult, - isPostTxFailureResult, - PostTxFailureResult, + isPostTxFailure, + PostTxFailure, PostTxResult, } from "./cosmwasmclient"; import { findAttribute, Log } from "./logs"; @@ -91,7 +91,7 @@ export interface ExecuteResult { readonly transactionHash: string; } -function createPostTxErrorMessage(result: PostTxFailureResult): string { +function createPostTxErrorMessage(result: PostTxFailure): string { return `Error when posting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`; } @@ -165,7 +165,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); - if (isPostTxFailureResult(result)) { + if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } const codeIdAttr = findAttribute(result.logs, "message", "code_id"); @@ -214,7 +214,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); - if (isPostTxFailureResult(result)) { + if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } const contractAddressAttr = findAttribute(result.logs, "message", "contract_address"); @@ -254,7 +254,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { }; const result = await this.postTx(signedTx); - if (isPostTxFailureResult(result)) { + if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } return { diff --git a/packages/cosmwasm/types/cosmwasmclient.d.ts b/packages/cosmwasm/types/cosmwasmclient.d.ts index 6ce66de0..0dad3fa0 100644 --- a/packages/cosmwasm/types/cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cosmwasmclient.d.ts @@ -14,24 +14,22 @@ export interface Account { readonly accountNumber: number; readonly sequence: number; } -export interface PostTxFailureResult { +export interface PostTxFailure { /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; - readonly height: string; + readonly height: number; readonly code: number; readonly rawLog: string; } -export interface PostTxSuccessResult { +export interface PostTxSuccess { readonly logs: readonly Log[]; readonly rawLog: string; /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ readonly transactionHash: string; readonly data?: Uint8Array; } -export declare type PostTxResult = PostTxSuccessResult | PostTxFailureResult; -export declare function isPostTxFailureResult( - postTxResult: PostTxResult, -): postTxResult is PostTxFailureResult; +export declare type PostTxResult = PostTxSuccess | PostTxFailure; +export declare function isPostTxFailure(postTxResult: PostTxResult): postTxResult is PostTxFailure; export interface SearchByIdQuery { readonly id: string; }