From 34d19e4d7e25cd2827864a7515f51259c0d19625 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 9 Jun 2020 16:46:10 +0100 Subject: [PATCH] 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; }