diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index abf79c5f..7e2cecbf 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { Random } from "@iov/crypto"; +import { Random, Sha256 } from "@iov/crypto"; import { Bech32, Encoding } from "@iov/encoding"; import { encodeSecp256k1Signature, makeSignBytes, marshalTx } from "./encoding"; @@ -85,13 +85,17 @@ function makeRandomAddress(): string { return Bech32.encode("cosmos", Random.getBytes(20)); } -async function uploadContract(client: RestClient, pen: Pen): Promise { +async function uploadCustomContract( + client: RestClient, + pen: Pen, + wasmCode: Uint8Array, +): Promise { const memo = "My first contract on chain"; const theMsg: MsgStoreCode = { type: "wasm/store-code", value: { sender: faucet.address, - wasm_byte_code: toBase64(getRandomizedContract()), + wasm_byte_code: toBase64(wasmCode), source: "https://github.com/confio/cosmwasm/raw/0.7/lib/vm/testdata/contract_0.6.wasm", builder: "cosmwasm-opt:0.6.2", }, @@ -113,6 +117,10 @@ async function uploadContract(client: RestClient, pen: Pen): Promise { + return uploadCustomContract(client, pen, getRandomizedContract()); +} + async function instantiateContract( client: RestClient, pen: Pen, @@ -223,9 +231,8 @@ describe("RestClient", () => { }); }); - // TODO: re-enable when stable // this is failing for me on first run (faucet has not signed anything) - xit("has correct pubkey for faucet", async () => { + it("has correct pubkey for faucet", async () => { pendingWithoutCosmos(); const client = new RestClient(httpUrl); const { result } = await client.authAccounts(faucet.address); @@ -364,7 +371,8 @@ describe("RestClient", () => { const numExisting = existingInfos.length; // upload data - const result = await uploadContract(client, pen); + const wasmCode = getRandomizedContract(); + const result = await uploadCustomContract(client, pen, wasmCode); expect(result.code).toBeFalsy(); const logs = parseLogs(result.logs); const codeIdAttr = findAttribute(logs, "message", "code_id"); @@ -377,10 +385,13 @@ describe("RestClient", () => { expect(lastInfo.id).toEqual(codeId); expect(lastInfo.creator).toEqual(faucet.address); - // TODO: check code hash matches expectation - // expect(lastInfo.code_hash).toEqual(faucet.address); + // check code hash matches expectation + const wasmHash = new Sha256(wasmCode).digest(); + expect(lastInfo.code_hash.toLowerCase()).toEqual(toHex(wasmHash)); - // TODO: download code and check against auto-gen + // download code and check against auto-gen + const download = await client.getCode(codeId); + expect(download).toEqual(wasmCode); }); it("can list contracts and get info", async () => { diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 632f0b5b..91b4647e 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -45,11 +45,11 @@ interface AuthAccountsResponse { // Currently all wasm query responses return json-encoded strings... // later deprecate this and use the specific types for result // (assuming it is inlined, no second parse needed) -type WasmResponse = WasmSuccess | WasmError; +type WasmResponse = WasmSuccess | WasmError; -interface WasmSuccess { +interface WasmSuccess { readonly height: string; - readonly result: string; + readonly result: T; } interface WasmError { @@ -92,6 +92,11 @@ interface EncodeTxResponse { readonly tx: string; } +interface GetCodeResult { + // base64 encoded wasm + readonly code: string; +} + type RestClientResponse = | NodeInfoResponse | BlocksResponse @@ -100,15 +105,23 @@ type RestClientResponse = | SearchTxsResponse | PostTxsResponse | EncodeTxResponse - | WasmResponse; + | WasmResponse + | WasmResponse; type BroadcastMode = "block" | "sync" | "async"; -function isWasmError(resp: WasmResponse): resp is WasmError { +function isWasmError(resp: WasmResponse): resp is WasmError { return (resp as WasmError).error !== undefined; } -function parseWasmResponse(response: WasmResponse): any { +function unwrapWasmResponse(response: WasmResponse): T { + if (isWasmError(response)) { + throw new Error(response.error); + } + return response.result; +} + +function parseWasmResponse(response: WasmResponse): any { if (isWasmError(response)) { throw new Error(response.error); } @@ -237,10 +250,9 @@ export class RestClient { // this will download the original wasm bytecode by code id // throws error if no code with this id public async getCode(id: number): Promise { - // TODO: broken currently const path = `/wasm/code/${id}`; - const responseData = await this.get(path); - const { code } = parseWasmResponse(responseData as WasmResponse); + const responseData = (await this.get(path)) as WasmResponse; + const { code } = unwrapWasmResponse(responseData); return fromBase64(code); } diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index a7fbd87f..d560856f 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -30,10 +30,10 @@ interface AuthAccountsResponse { readonly value: CosmosSdkAccount; }; } -declare type WasmResponse = WasmSuccess | WasmError; -interface WasmSuccess { +declare type WasmResponse = WasmSuccess | WasmError; +interface WasmSuccess { readonly height: string; - readonly result: string; + readonly result: T; } interface WasmError { readonly error: string; @@ -68,6 +68,9 @@ export interface PostTxsResponse { interface EncodeTxResponse { readonly tx: string; } +interface GetCodeResult { + readonly code: string; +} declare type RestClientResponse = | NodeInfoResponse | BlocksResponse @@ -76,7 +79,8 @@ declare type RestClientResponse = | SearchTxsResponse | PostTxsResponse | EncodeTxResponse - | WasmResponse; + | WasmResponse + | WasmResponse; declare type BroadcastMode = "block" | "sync" | "async"; export declare class RestClient { private readonly client;