From 95e1e85a264f7ba840f18abf7147f842bf252e4a Mon Sep 17 00:00:00 2001 From: Luke Wilson Date: Fri, 28 Oct 2022 11:24:49 +0100 Subject: [PATCH 1/2] Fix types import --- package.json | 2 +- src/instance.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 915f069..eaa0094 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@terran-one/cosmwasm-vm-js", - "version": "0.2.13", + "version": "0.2.14", "license": "MIT", "author": "TerranOne", "main": "dist/index.js", diff --git a/src/instance.ts b/src/instance.ts index 2845acd..cbe9b7c 100644 --- a/src/instance.ts +++ b/src/instance.ts @@ -3,7 +3,7 @@ import { bech32, BechLib } from 'bech32'; import { Region } from './memory'; import { ecdsaRecover, ecdsaVerify } from 'secp256k1'; import { IBackend, Record } from './backend'; -import { Env, MessageInfo } from 'types'; +import { Env, MessageInfo } from './types'; import { toByteArray, toNumber } from './helpers/byte-array'; export const MAX_LENGTH_DB_KEY: number = 64 * 1024; From c200ecb35fe5552de2ca7ae1561327c63de8c60e Mon Sep 17 00:00:00 2001 From: William Chen Date: Mon, 31 Oct 2022 03:15:58 -0700 Subject: [PATCH 2/2] add debugMsgs to VMInstance, make BasicQuerier generic --- src/backend/backendApi.ts | 4 +-- src/backend/querier.ts | 41 ++++++-------------------- src/backend/storage.ts | 5 ++-- src/instance.ts | 7 ++--- test/integration/burner.test.ts | 8 ++++- test/integration/hackatom.test.ts | 49 ++++++++++++++++++++++++------- 6 files changed, 63 insertions(+), 51 deletions(-) diff --git a/src/backend/backendApi.ts b/src/backend/backendApi.ts index 3367b24..65877a9 100644 --- a/src/backend/backendApi.ts +++ b/src/backend/backendApi.ts @@ -29,12 +29,12 @@ export class GasInfo implements IGasInfo { } export interface IBackendApi { + bech32_prefix: string; canonical_address(human: string): Uint8Array; - human_address(canonical: Uint8Array): string; } -export class BasicBackendApi implements BasicBackendApi { +export class BasicBackendApi implements IBackendApi { // public GAS_COST_CANONICALIZE = 55; public CANONICAL_LENGTH = 54; public EXCESS_PADDING = 6; diff --git a/src/backend/querier.ts b/src/backend/querier.ts index dd5f1c5..33bb5d4 100644 --- a/src/backend/querier.ts +++ b/src/backend/querier.ts @@ -1,51 +1,28 @@ export interface IQuerier { query_raw(request: Uint8Array, gas_limit: number /* Uint64 */): Uint8Array; - update_balance(addr: string, balance: { amount: string, denom: string }[]): { amount: string, denom: string }[]; } export class BasicQuerier implements IQuerier { - private balances: Map = new Map(); - - constructor() { - this.query_raw = this.query_raw.bind(this); - } - - update_balance(addr: string, balance: { amount: string; denom: string; }[]): { amount: string; denom: string; }[] { - this.balances.set(addr, balance); - return balance; - } // eslint-disable-next-line @typescript-eslint/no-unused-vars query_raw(request: Uint8Array, gas_limit: number): Uint8Array { - const [query, type] = parseQuery(request); + const queryRequest = parseQuery(request); - switch (type) { - case QueryType.AllBalances: - const address = query.bank.all_balances.address as string; - const balances = { amount: this.balances.get(address) || [] }; - return objectToUint8Array({ok: {ok: objectToBase64(balances)}}); + // TODO: make room for error + // The Ok(Ok(x)) represents SystemResult> - default: - throw new Error('Not implemented'); - } + return objectToUint8Array({ ok: { ok: objectToBase64(this.handleQuery(queryRequest)) }}); + } - // ToDo: gas + handleQuery(queryRequest: any): any { + throw new Error(`Unimplemented - subclass BasicQuerier and provide handleQuery() implementation.`) } } -enum QueryType { AllBalances } -function parseQuery(bytes: Uint8Array): [any, QueryType] { +function parseQuery(bytes: Uint8Array): any { const query = JSON.parse(new TextDecoder().decode(bytes)); - return [query, queryType(query)]; -} - -function queryType(query: any): QueryType { - if (query.bank?.all_balances) { - return QueryType.AllBalances; - } - - throw new Error('Not implemented'); + return query; } function objectToBase64(obj: object): string { diff --git a/src/backend/storage.ts b/src/backend/storage.ts index c3fb8a2..fe15d9f 100644 --- a/src/backend/storage.ts +++ b/src/backend/storage.ts @@ -4,6 +4,7 @@ import Immutable from 'immutable'; import { MAX_LENGTH_DB_KEY } from '../instance'; export interface IStorage { + dict: Immutable.Map; get(key: Uint8Array): Uint8Array | null; set(key: Uint8Array, value: Uint8Array): void; @@ -71,8 +72,8 @@ export class BasicKVStorage implements IStorage { } export class BasicKVIterStorage extends BasicKVStorage implements IIterStorage { - constructor(public iterators: Map = new Map()) { - super(); + constructor(public dict: Immutable.Map = Immutable.Map(), public iterators: Map = new Map()) { + super(dict); } all(iterator_id: Uint8Array): Array { diff --git a/src/instance.ts b/src/instance.ts index cbe9b7c..274cb27 100644 --- a/src/instance.ts +++ b/src/instance.ts @@ -12,9 +12,9 @@ export const MAX_LENGTH_CANONICAL_ADDRESS: number = 64; export const MAX_LENGTH_HUMAN_ADDRESS: number = 256; export class VMInstance { - public PREFIX: string = 'terra'; public instance?: WebAssembly.Instance; public bech32: BechLib; + public debugMsgs: string[] = []; constructor(public backend: IBackend, public readonly gasLimit?: number | undefined) { this.bech32 = bech32; @@ -336,9 +336,8 @@ export class VMInstance { throw new Error('Invalid address.'); } - // TODO: Change prefix to be configurable per environment const human = this.bech32.encode( - this.PREFIX, + this.backend.backend_api.bech32_prefix, this.bech32.toWords(canonical) ); if (human !== source.str) { @@ -453,7 +452,7 @@ export class VMInstance { } do_debug(message: Region) { - console.log(message.read_str()); + this.debugMsgs.push(message.read_str()); } do_query_chain(request: Region): Region { diff --git a/test/integration/burner.test.ts b/test/integration/burner.test.ts index 08a49bd..f3703ce 100644 --- a/test/integration/burner.test.ts +++ b/test/integration/burner.test.ts @@ -17,11 +17,17 @@ import { import { toAscii } from '@cosmjs/encoding'; import { Env, MessageInfo } from '../../src/types'; +class MockQuerier extends BasicQuerier { + handleQuery(request: any): any { + return { amount: [{ denom: 'earth', amount: '1000' }] } + } +} + const wasmBytecode = readFileSync('testdata/v1.1/burner.wasm'); const backend: IBackend = { backend_api: new BasicBackendApi('terra'), storage: new BasicKVIterStorage(), - querier: new BasicQuerier(), + querier: new MockQuerier(), }; const creator = 'terra1337xewwfv3jdjuz8e0nea9vd8dpugc0k2dcyt3'; diff --git a/test/integration/hackatom.test.ts b/test/integration/hackatom.test.ts index 28e6f5f..77e48eb 100644 --- a/test/integration/hackatom.test.ts +++ b/test/integration/hackatom.test.ts @@ -4,18 +4,39 @@ import { BasicBackendApi, BasicKVIterStorage, BasicQuerier, - IBackend, } from '../../src/backend'; import { fromBase64 } from '@cosmjs/encoding'; import { Region } from '../../src/memory'; import { expectResponseToBeOk, parseBase64Response } from '../common/test-vm'; +type HackatomQueryRequest = { + bank: { + all_balances: { + address: string + } + } +} +class HackatomMockQuerier extends BasicQuerier { + private balances: Map = new Map(); + + update_balance(addr: string, balance: { amount: string; denom: string; }[]): { amount: string; denom: string; }[] { + this.balances.set(addr, balance); + return balance; + } + + handleQuery(queryRequest: HackatomQueryRequest): any { + if ('bank' in queryRequest) { + if ('all_balances' in queryRequest.bank) { + const { address } = queryRequest.bank.all_balances; + return { amount: this.balances.get(address) || [] } + } + } + + throw new Error(`unknown query: ${JSON.stringify(queryRequest)}`); + } +} + const wasmBytecode = readFileSync('testdata/v1.1/hackatom.wasm'); -const backend: IBackend = { - backend_api: new BasicBackendApi('terra'), - storage: new BasicKVIterStorage(), - querier: new BasicQuerier(), -}; const verifier = 'terra1kzsrgcktshvqe9p089lqlkadscqwkezy79t8y9'; const beneficiary = 'terra1zdpgj8am5nqqvht927k3etljyl6a52kwqup0je'; @@ -38,11 +59,19 @@ const mockInfo: { sender: string, funds: { amount: string, denom: string }[] } = let vm: VMInstance; describe('hackatom', () => { + let querier: HackatomMockQuerier; + beforeEach(async () => { - vm = new VMInstance(backend); + querier = new HackatomMockQuerier(); + vm = new VMInstance({ + backend_api: new BasicBackendApi('terra'), + storage: new BasicKVIterStorage(), + querier + }); await vm.build(wasmBytecode); }); + it('proper_initialization', async () => { // Act const instantiateResponse = vm.instantiate(mockEnv, mockInfo, { verifier, beneficiary }); @@ -93,7 +122,7 @@ describe('hackatom', () => { // Arrange const richAddress = 'foobar'; const richBalance = [{ amount: '10000', denom: 'gold' }]; - vm.backend.querier.update_balance(richAddress, richBalance); + querier.update_balance(richAddress, richBalance); vm.instantiate(mockEnv, mockInfo, { verifier, beneficiary }); @@ -123,7 +152,7 @@ describe('hackatom', () => { it('execute_release_works', async () => { // Arrange vm.instantiate(mockEnv, mockInfo, { verifier, beneficiary }); - vm.backend.querier.update_balance(mockContractAddr, [{ amount: '1000', denom: 'earth' }]); + querier.update_balance(mockContractAddr, [{ amount: '1000', denom: 'earth' }]); // Act const execResponse = vm.execute( @@ -149,7 +178,7 @@ describe('hackatom', () => { it('execute_release_fails_for_wrong_sender', async () => { // Arrange vm.instantiate(mockEnv, mockInfo, { verifier, beneficiary }); - vm.backend.querier.update_balance(mockContractAddr, [{ amount: '1000', denom: 'earth' }]); + querier.update_balance(mockContractAddr, [{ amount: '1000', denom: 'earth' }]); // Act const execResponse = vm.execute(