Skip to content
This repository has been archived by the owner on Oct 6, 2021. It is now read-only.

Better error for Service.at #291

Merged
merged 1 commit into from
Sep 12, 2019
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
1 change: 0 additions & 1 deletion packages/gateway/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,6 @@ class HttpGateway implements OasisGateway {
address: bytes.toHex(request.address!),
}
);
// todo: throw cleaner error when code doesn't exist
return {
code: bytes.parseHex(response.code),
};
Expand Down
14 changes: 14 additions & 0 deletions packages/service/src/error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { bytes } from '@oasislabs/common';
import { RpcOptions } from './oasis-gateway';

export function NO_CODE_ERROR_MSG(address: Uint8Array): string {
return `
No code exists for address ${bytes.toHex(address)}.
Either your address is incorrect or the deploy failed.
`;
}

export class RpcError extends Error {
constructor(
readonly rpcArgs: any[],
Expand All @@ -15,3 +23,9 @@ export class DeployError extends Error {
super(...params);
}
}

export class ServiceError extends Error {
constructor(readonly address: Uint8Array, ...params: any[]) {
super(...params);
}
}
2 changes: 1 addition & 1 deletion packages/service/src/oasis-gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type GetCodeRequest = {
};

export type GetCodeResponse = {
code: Uint8Array;
code: Uint8Array | null;
};

export type RpcRequest = {
Expand Down
9 changes: 7 additions & 2 deletions packages/service/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import camelCase from 'camelcase';
import { KeyStore } from '@oasislabs/confidential';
import { bytes } from '@oasislabs/common';

import { RpcError } from './error';
import { RpcError, ServiceError, NO_CODE_ERROR_MSG } from './error';
import { Idl, IdlError, RpcFn, RpcInput } from './idl';
import { ServiceOptions } from './service';
import { OasisGateway, RpcOptions } from './oasis-gateway';
Expand Down Expand Up @@ -183,7 +183,12 @@ export class RpcFactory {
): Promise<RpcCoder> {
// Check the contract's deploy header to see if it's confidential.
let response = await options.gateway!.getCode({ address });
let deployHeader = header.parseFromCode(response.code);

if (!response.code) {
throw new ServiceError(address, NO_CODE_ERROR_MSG(address));
}

let deployHeader = header.parseFromCode(response.code!);

if (!deployHeader || !deployHeader.body.confidential) {
return OasisCoder.plaintext();
Expand Down
11 changes: 10 additions & 1 deletion packages/service/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SubscribeRequest,
SubscribeTopic,
} from './oasis-gateway';
import { ServiceError, NO_CODE_ERROR_MSG } from './error';

/**
* Service is the object representation of an Oasis rpc service.
Expand Down Expand Up @@ -85,6 +86,11 @@ export default class Service {

options = Service.setupOptions(options);
let response = await options.gateway!.getCode({ address: _address });

if (!response.code) {
throw new ServiceError(_address, NO_CODE_ERROR_MSG(_address));
}

let wasm = DeployHeaderReader.initcode(response.code);
let idl = await fromWasm(wasm);
return new Service(idl, address, options);
Expand Down Expand Up @@ -157,7 +163,10 @@ export default class Service {
public removeEventListener(event: string, listener: Listener) {
let subscription = this._inner.subscriptions.get(event);
if (subscription === undefined) {
throw new Error(`no subscriptions found for ${event}`);
throw new ServiceError(
this._inner.address,
`no subscriptions found for ${event}`
);
}

this._inner.listeners.removeListener(event, listener);
Expand Down
28 changes: 28 additions & 0 deletions packages/service/test/service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from './utils';
import { OasisCoder } from '../src/coder/oasis';
import { makeExpectedBytecode } from './utils';
import { NO_CODE_ERROR_MSG } from '../src/error';

setGateway(new EmptyOasisGateway());

Expand Down Expand Up @@ -154,6 +155,33 @@ describe('Service', () => {

expect(bytes.parseHex(address)).toEqual(s._inner.address);
});

it('Service.at should give an informative error when an invalid address is used', async () => {
const address = bytes.parseHex(
'0x288e7e1cc60962f40d4d782950470e3705c5acf4'
);
const bin = bytes.toHex(
new Uint8Array(
require('fs').readFileSync('test/wasm/mantle-counter.wasm')
)
);
const gateway = {
// Null getCode response means the service doesn't exist at the address.
getCode: () => {
return { code: null };
},
};

try {
// @ts-ignore
await Service.at(address, {
gateway,
db: new DummyStorage(),
});
} catch (e) {
expect(e.message).toBe(NO_CODE_ERROR_MSG(address));
}
});
});

function confidentialCoder() {
Expand Down
5 changes: 3 additions & 2 deletions packages/web3/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ export default class Web3Gateway implements OasisGateway {
bytes.toHex(request.address),
'latest'
);
// todo: throw cleaner error when code doesn't exist

// Note: the gateway returns '0x' for all addresses without code.
return {
code: bytes.parseHex(response),
code: response === '0x' ? null : bytes.parseHex(response),
};
}

Expand Down