Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/backend/backendApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 9 additions & 32 deletions src/backend/querier.ts
Original file line number Diff line number Diff line change
@@ -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<string, { amount: string, denom: string }[]> = 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<ContractResult<Binary>>

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 {
Expand Down
5 changes: 3 additions & 2 deletions src/backend/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Immutable from 'immutable';
import { MAX_LENGTH_DB_KEY } from '../instance';

export interface IStorage {
dict: Immutable.Map<string, string>;
get(key: Uint8Array): Uint8Array | null;

set(key: Uint8Array, value: Uint8Array): void;
Expand Down Expand Up @@ -71,8 +72,8 @@ export class BasicKVStorage implements IStorage {
}

export class BasicKVIterStorage extends BasicKVStorage implements IIterStorage {
constructor(public iterators: Map<number, Iter> = new Map()) {
super();
constructor(public dict: Immutable.Map<string, string> = Immutable.Map(), public iterators: Map<number, Iter> = new Map()) {
super(dict);
}

all(iterator_id: Uint8Array): Array<Record> {
Expand Down
7 changes: 3 additions & 4 deletions src/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 7 additions & 1 deletion test/integration/burner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
49 changes: 39 additions & 10 deletions test/integration/hackatom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, { amount: string, denom: string }[]> = 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';
Expand All @@ -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 });
Expand Down Expand Up @@ -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 });

Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down