From 6f35fad87c411302fb9e733d3267646c134aecca Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Feb 2020 20:29:51 +0100 Subject: [PATCH 1/5] WIP: update return types for queries --- packages/sdk/src/restclient.spec.ts | 4 ++-- packages/sdk/src/restclient.ts | 16 ++++++++-------- packages/sdk/src/types.ts | 4 ++-- packages/sdk/types/restclient.d.ts | 4 ++-- packages/sdk/types/types.d.ts | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index 53312d5e..4f927d3b 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -23,7 +23,7 @@ import { StdTx, } from "./types"; -const { fromBase64, fromHex, toAscii, toBase64, toHex } = Encoding; +const { fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } = Encoding; const httpUrl = "http://localhost:1317"; const defaultNetworkId = "testing"; @@ -528,7 +528,7 @@ describe("RestClient", () => { // we can query the verifier properly const verifier = await client.queryContractSmart(contractAddress!, { verifier: {} }); - expect(verifier).toEqual(faucet.address); + expect(fromAscii(verifier)).toEqual(faucet.address); // invalid query syntax throws an error await client.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 14809dae..503f1543 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -308,23 +308,23 @@ export class RestClient { // This is an empty array if no such contract, or contract has no data. public async getAllContractState(address: string): Promise { const path = `/wasm/contract/${address}/state`; - const responseData = await this.get(path); - return parseWasmResponse(responseData as WasmResponse); + const responseData = (await this.get(path)) as WasmResponse; + return unwrapWasmResponse(responseData); } // Returns the data at the key if present (unknown decoded json), // or null if no data at this (contract address, key) pair - public async queryContractRaw(address: string, key: Uint8Array): Promise { + public async queryContractRaw(address: string, key: Uint8Array): Promise { const hexKey = toHex(key); const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`; - const responseData = await this.get(path); - const data: readonly WasmData[] = parseWasmResponse(responseData as WasmResponse); - return data.length === 0 ? null : data[0].val; + const responseData = (await this.get(path)) as WasmResponse; + const data = unwrapWasmResponse(responseData); + return data.length === 0 ? null : fromBase64(data[0].val); } // Makes a "smart query" on the contract, returns response verbatim (json.RawMessage) // Throws error if no such contract or invalid query format - public async queryContractSmart(address: string, query: object): Promise { + public async queryContractSmart(address: string, query: object): Promise { const encoded = toHex(toUtf8(JSON.stringify(query))); const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; const responseData = (await this.get(path)) as WasmResponse; @@ -332,6 +332,6 @@ export class RestClient { throw new Error(responseData.error); } // no extra parse here for now, see https://github.com/confio/cosmwasm/issues/144 - return responseData.result; + return fromBase64(responseData.result); } } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index d9ac9998..1d1c5cb2 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -190,6 +190,6 @@ export interface ContractInfo { export interface WasmData { // key is hex-encoded readonly key: string; - // value can be any decoded json, often an object but can be anything - readonly val: unknown; + // value is base64 encoded + readonly val: string; } diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index ad5f735d..eb9c7522 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -103,7 +103,7 @@ export declare class RestClient { listContractsByCodeId(id: number): Promise; getContractInfo(address: string): Promise; getAllContractState(address: string): Promise; - queryContractRaw(address: string, key: Uint8Array): Promise; - queryContractSmart(address: string, query: object): Promise; + queryContractRaw(address: string, key: Uint8Array): Promise; + queryContractSmart(address: string, query: object): Promise; } export {}; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 20b65f4d..791758cf 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -141,6 +141,6 @@ export interface ContractInfo { } export interface WasmData { readonly key: string; - readonly val: unknown; + readonly val: string; } export {}; From 5677976463dac1ea5b6900140f166d98e2eb1e7e Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 09:54:15 +0100 Subject: [PATCH 2/5] Tests pass --- packages/sdk/src/restclient.spec.ts | 17 ++++++++++------- packages/sdk/src/restclient.ts | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index 4f927d3b..b470ee0d 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -468,7 +468,7 @@ describe("RestClient", () => { .catch(error => expect(error).toMatch(`No contract with address ${beneficiaryAddress}`)); }); - describe("contract state", () => { + fdescribe("contract state", () => { const client = new RestClient(httpUrl); const noContract = makeRandomAddress(); const expectedKey = toAscii("config"); @@ -497,8 +497,10 @@ describe("RestClient", () => { expect(state.length).toEqual(1); const data = state[0]; expect(data.key.toLowerCase()).toEqual(toHex(expectedKey)); - expect((data.val as any).verifier).toBeDefined(); - expect((data.val as any).beneficiary).toBeDefined(); + const value = JSON.parse(fromAscii(fromBase64(data.val))); + console.log(value); + expect(value.verifier).toBeDefined(); + expect(value.beneficiary).toBeDefined(); // bad address is empty array const noContractState = await client.getAllContractState(noContract); @@ -509,10 +511,11 @@ describe("RestClient", () => { pendingWithoutCosmos(); // query by one key - const model = await client.queryContractRaw(contractAddress!, expectedKey); - expect(model).not.toBeNull(); - expect((model as any).verifier).toBeDefined(); - expect((model as any).beneficiary).toBeDefined(); + const raw = await client.queryContractRaw(contractAddress!, expectedKey); + expect(raw).not.toBeNull(); + const model = JSON.parse(fromAscii(raw!)); + expect(model.verifier).toBeDefined(); + expect(model.beneficiary).toBeDefined(); // missing key is null const missing = await client.queryContractRaw(contractAddress!, fromHex("cafe0dad")); diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 503f1543..3de93e49 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -97,6 +97,11 @@ interface GetCodeResult { readonly code: string; } +interface SmartQueryResponse { + // base64 encoded response + readonly smart: string; +} + type RestClientResponse = | NodeInfoResponse | BlocksResponse @@ -309,7 +314,12 @@ export class RestClient { public async getAllContractState(address: string): Promise { const path = `/wasm/contract/${address}/state`; const responseData = (await this.get(path)) as WasmResponse; - return unwrapWasmResponse(responseData); + console.log("all state"); + console.log(responseData); + console.log("***"); + const r = unwrapWasmResponse(responseData); + console.log(r); + return r || []; } // Returns the data at the key if present (unknown decoded json), @@ -327,11 +337,10 @@ export class RestClient { public async queryContractSmart(address: string, query: object): Promise { const encoded = toHex(toUtf8(JSON.stringify(query))); const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; - const responseData = (await this.get(path)) as WasmResponse; - if (isWasmError(responseData)) { - throw new Error(responseData.error); - } + const responseData = (await this.get(path)) as WasmResponse; + const result = unwrapWasmResponse(responseData); // no extra parse here for now, see https://github.com/confio/cosmwasm/issues/144 - return fromBase64(responseData.result); + console.log(result); + return fromBase64(result.smart); } } From 13c708e78717d5931606c9b94cf904a06802ea8f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 10:03:20 +0100 Subject: [PATCH 3/5] Fix malformed REST response/parsing --- packages/sdk/src/restclient.spec.ts | 7 +++---- packages/sdk/src/restclient.ts | 21 +++++++++++++-------- packages/sdk/src/types.ts | 17 +++++++++++++++++ packages/sdk/types/restclient.d.ts | 4 ++-- packages/sdk/types/types.d.ts | 5 +++++ 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index b470ee0d..d4dcab0c 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -468,7 +468,7 @@ describe("RestClient", () => { .catch(error => expect(error).toMatch(`No contract with address ${beneficiaryAddress}`)); }); - fdescribe("contract state", () => { + describe("contract state", () => { const client = new RestClient(httpUrl); const noContract = makeRandomAddress(); const expectedKey = toAscii("config"); @@ -496,9 +496,8 @@ describe("RestClient", () => { const state = await client.getAllContractState(contractAddress!); expect(state.length).toEqual(1); const data = state[0]; - expect(data.key.toLowerCase()).toEqual(toHex(expectedKey)); - const value = JSON.parse(fromAscii(fromBase64(data.val))); - console.log(value); + expect(data.key).toEqual(expectedKey); + const value = JSON.parse(fromAscii(data.val)); expect(value.verifier).toBeDefined(); expect(value.beneficiary).toBeDefined(); diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 3de93e49..14857bdb 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,7 +1,17 @@ import { Encoding } from "@iov/encoding"; import axios, { AxiosError, AxiosInstance } from "axios"; -import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, isAminoStdTx, StdTx, WasmData } from "./types"; +import { + AminoTx, + CodeInfo, + ContractInfo, + CosmosSdkAccount, + isAminoStdTx, + Model, + parseWasmData, + StdTx, + WasmData, +} from "./types"; const { fromBase64, fromUtf8, toHex, toUtf8 } = Encoding; @@ -311,15 +321,11 @@ export class RestClient { // Returns all contract state. // This is an empty array if no such contract, or contract has no data. - public async getAllContractState(address: string): Promise { + public async getAllContractState(address: string): Promise { const path = `/wasm/contract/${address}/state`; const responseData = (await this.get(path)) as WasmResponse; - console.log("all state"); - console.log(responseData); - console.log("***"); const r = unwrapWasmResponse(responseData); - console.log(r); - return r || []; + return r ? r.map(parseWasmData) : []; } // Returns the data at the key if present (unknown decoded json), @@ -340,7 +346,6 @@ export class RestClient { const responseData = (await this.get(path)) as WasmResponse; const result = unwrapWasmResponse(responseData); // no extra parse here for now, see https://github.com/confio/cosmwasm/issues/144 - console.log(result); return fromBase64(result.smart); } } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 1d1c5cb2..c7fcfe0f 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,3 +1,7 @@ +import { Encoding } from "@iov/encoding"; + +const { fromBase64, fromHex } = Encoding; + // We will move all needed *interfaces* from amino-js here // This means bcp can just import them from here (if needed at all) export interface Tx { @@ -193,3 +197,16 @@ export interface WasmData { // value is base64 encoded readonly val: string; } + +// Model is a parsed WasmData object +export interface Model { + readonly key: Uint8Array; + readonly val: Uint8Array; +} + +export function parseWasmData({ key, val }: WasmData): Model { + return { + key: fromHex(key), + val: fromBase64(val), + }; +} diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index eb9c7522..b83f731a 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,4 +1,4 @@ -import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, StdTx, WasmData } from "./types"; +import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, Model, StdTx } from "./types"; interface NodeInfo { readonly network: string; } @@ -102,7 +102,7 @@ export declare class RestClient { listContractAddresses(): Promise; listContractsByCodeId(id: number): Promise; getContractInfo(address: string): Promise; - getAllContractState(address: string): Promise; + getAllContractState(address: string): Promise; queryContractRaw(address: string, key: Uint8Array): Promise; queryContractSmart(address: string, query: object): Promise; } diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 791758cf..9d61f398 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -143,4 +143,9 @@ export interface WasmData { readonly key: string; readonly val: string; } +export interface Model { + readonly key: Uint8Array; + readonly val: Uint8Array; +} +export declare function parseWasmData({ key, val }: WasmData): Model; export {}; From 8e0042ed751d889cb9fcc344fc14daa178eb272f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 10:23:57 +0100 Subject: [PATCH 4/5] Fix bcp, update to proper docker image --- packages/bcp/src/cosmwasmconnection.ts | 7 +++---- scripts/cosm/env | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 5640c72f..fbeedbf2 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -41,7 +41,7 @@ import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; import { accountToNonce, BankToken, Erc20Token } from "./types"; -const { toHex } = Encoding; +const { fromAscii, toHex } = Encoding; interface ChainData { readonly chainId: ChainId; @@ -168,9 +168,8 @@ export class CosmWasmConnection implements BlockchainConnection { this.erc20Tokens.map( async (erc20): Promise => { const queryMsg = { balance: { address: address } }; - const response = JSON.parse( - await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg), - ); + const smart = await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg); + const response = JSON.parse(fromAscii(smart)); const normalizedBalance = new BN(response.balance).toString(); return { fractionalDigits: erc20.fractionalDigits, diff --git a/scripts/cosm/env b/scripts/cosm/env index b24084da..70eb1944 100644 --- a/scripts/cosm/env +++ b/scripts/cosm/env @@ -1,5 +1,5 @@ # Choose from https://hub.docker.com/r/cosmwasm/wasmd-demo/tags REPOSITORY="cosmwasm/wasmd-demo" -VERSION="v0.0.5" +VERSION="v0.6.0" CONTAINER_NAME="wasmd" From 3a9cf566352476d6f510d715b3fb3feded94666d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 11:31:27 +0100 Subject: [PATCH 5/5] Use assert to avoid ! --- packages/sdk/src/restclient.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index d4dcab0c..3cb01de3 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -511,8 +511,8 @@ describe("RestClient", () => { // query by one key const raw = await client.queryContractRaw(contractAddress!, expectedKey); - expect(raw).not.toBeNull(); - const model = JSON.parse(fromAscii(raw!)); + assert(raw, "must get result"); + const model = JSON.parse(fromAscii(raw)); expect(model.verifier).toBeDefined(); expect(model.beneficiary).toBeDefined();