Skip to content

Commit

Permalink
feat: pass errors between contracts as they were generated in the ori…
Browse files Browse the repository at this point in the history
…gin contract

gh-309
  • Loading branch information
rpiszczatowski committed Mar 10, 2023
1 parent 1c55d96 commit fcafd14
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 43 deletions.
7 changes: 4 additions & 3 deletions src/core/modules/impl/HandlerExecutorFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import { Buffer } from 'warp-isomorphic';
// eslint-disable-next-line
const BigNumber = require('bignumber.js');

export class ContractError extends Error {
constructor(message, readonly subtype?: string) {
super(message);
export class ContractError<T> extends Error {
constructor(public error: T, readonly subtype?: string) {
super('ContractError');
this.name = 'ContractError';
}
}
Expand Down Expand Up @@ -277,6 +277,7 @@ export type HandlerResult<State, Result> = {
export type InteractionResult<State, Result> = HandlerResult<State, Result> & {
type: InteractionResultType;
errorMessage?: string;
error?: unknown;
originalValidity?: Record<string, boolean>;
originalErrorMessages?: Record<string, string>;
};
Expand Down
52 changes: 12 additions & 40 deletions src/core/modules/impl/handler/WasmHandlerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ContractDefinition } from '../../../../core/ContractDefinition';
import { ExecutionContext } from '../../../../core/ExecutionContext';
import { EvalStateResult } from '../../../../core/modules/StateEvaluator';
import { SmartWeaveGlobal } from '../../../../legacy/smartweave-global';
import { ContractInteraction, InteractionData, InteractionResult } from '../HandlerExecutorFactory';
import { ContractError, ContractInteraction, InteractionData, InteractionResult } from '../HandlerExecutorFactory';
import { AbstractContractHandler } from './AbstractContractHandler';

export class WasmHandlerApi<State> extends AbstractContractHandler<State> {
Expand Down Expand Up @@ -44,32 +44,21 @@ export class WasmHandlerApi<State> extends AbstractContractHandler<State> {
};
} catch (e) {
await this.swGlobal.kv.rollback();
// note: as exceptions handling in WASM is currently somewhat non-existent
// https://www.assemblyscript.org/status.html#exceptions
// and since we have to somehow differentiate different types of exceptions
// - each exception message has to have a proper prefix added.

// exceptions with prefix [RE:] ("Runtime Exceptions") should break the execution immediately
// - eg: [RE:OOG] - [RuntimeException: OutOfGas]

// exception with prefix [CE:] ("Contract Exceptions") should be logged, but should not break
// the state evaluation - as they are considered as contracts' business exception (eg. validation errors)
// - eg: [CE:ITT] - [ContractException: InvalidTokenTransfer]
const result = {
errorMessage: e.message,
state: currentResult.state,
result: null
};
if (e.message.startsWith('[RE:')) {
this.logger.fatal(e);
if (e instanceof ContractError) {
return {
...result,
type: 'exception'
error: e.error,
type: 'error'
};
} else {
return {
...result,
type: 'error'
type: 'exception'
};
}
} finally {
Expand Down Expand Up @@ -100,32 +89,15 @@ export class WasmHandlerApi<State> extends AbstractContractHandler<State> {
handleResult = await this.wasmExports.warpContractView(action.input);
}

if (Object.prototype.hasOwnProperty.call(handleResult, 'WriteResponse')) {
return handleResult.WriteResponse;
if (!handleResult) {
return;
}
if (Object.prototype.hasOwnProperty.call(handleResult, 'ViewResponse')) {
return handleResult.ViewResponse;
}
{
this.logger.error('Error from rust', handleResult);
let errorKey;
let errorArgs = '';
if (typeof handleResult.Err === 'string' || handleResult.Err instanceof String) {
errorKey = handleResult.Err;
} else if ('kind' in handleResult.Err) {
errorKey = handleResult.Err.kind;
errorArgs = 'data' in handleResult.Err ? ' ' + handleResult.Err.data : '';
} else {
errorKey = Object.keys(handleResult.Err)[0];
errorArgs = ' ' + handleResult.Err[errorKey];
}

if (errorKey == 'RuntimeError') {
throw new Error(`[RE:RE]${errorArgs}`);
} else {
throw new Error(`[CE:${errorKey}${errorArgs}]`);
}
if (handleResult.type === 'ok') {
return handleResult.result;
}
this.logger.error('Error from rust', handleResult);
if (handleResult.type === 'error') throw new ContractError(handleResult.error);
throw new Error(handleResult.errorMessage);
}
default: {
throw new Error(`Support for ${this.contractDefinition.srcWasmLang} not implemented yet.`);
Expand Down

0 comments on commit fcafd14

Please sign in to comment.