Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add specific error classes for SDKClient and MirrorNodeCLient #382

Merged
merged 10 commits into from
Jul 27, 2022
4 changes: 2 additions & 2 deletions packages/relay/src/lib/clients/mirrorNodeClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
*/

import Axios, { AxiosInstance } from 'axios';
import { predefined } from '../errors';
import { MirrorNodeClientError } from '../errors';
import { Logger } from "pino";
import constants from './../constants';
import { Histogram, Registry } from 'prom-client';
Expand Down Expand Up @@ -167,7 +167,7 @@ export class MirrorNodeClient {
}

this.logger.error(new Error(error.message), `[GET] ${path} ${effectiveStatusCode} status`);
throw predefined.INTERNAL_ERROR;
throw new MirrorNodeClientError(error.message, effectiveStatusCode);
}

public async getAccountLatestTransactionByAddress(idOrAliasOrEvmAddress: string): Promise<object> {
Expand Down
28 changes: 9 additions & 19 deletions packages/relay/src/lib/clients/sdkClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { BigNumber } from '@hashgraph/sdk/lib/Transfer';
import { Logger } from "pino";
import { Gauge, Histogram, Registry } from 'prom-client';
import constants from './../constants';
import { SDKClientError } from '../errors';

const _ = require('lodash');

Expand Down Expand Up @@ -165,7 +166,7 @@ export class SDKClient {
async getTinyBarGasFee(callerName: string): Promise<number> {
const feeSchedules = await this.getFeeSchedule(callerName);
if (_.isNil(feeSchedules.current) || feeSchedules.current?.transactionFeeSchedule === undefined) {
throw new Error('Invalid FeeSchedules proto format');
throw new SDKClientError('Invalid FeeSchedules proto format');
}

for (const schedule of feeSchedules.current?.transactionFeeSchedule) {
Expand All @@ -177,7 +178,7 @@ export class SDKClient {
}
}

throw new Error(`${constants.ETH_FUNCTIONALITY_CODE} code not found in feeSchedule`);
throw new SDKClientError(`${constants.ETH_FUNCTIONALITY_CODE} code not found in feeSchedule`);
}

async getFileIdBytes(address: string, callerName: string): Promise<Uint8Array> {
Expand Down Expand Up @@ -261,11 +262,7 @@ export class SDKClient {
query._queryPayment?.toTinybars().toNumber(),
callerName);

if (e.status && e.status._code) {
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(e.message);
}

throw e;
throw new SDKClientError(e.message, e?.status?._code);
}
};

Expand All @@ -287,19 +284,14 @@ export class SDKClient {
0,
callerName);

// capture sdk transaction response errors and shorten familiar stack trace
if (e.status && e.status._code) {
throw new Error(e.message);
}

throw e;
throw new SDKClientError(e.message, e?.status?._code);
}
};

executeGetTransactionRecord = async (resp: TransactionResponse, transactionName: string, callerName: string): Promise<TransactionRecord> => {
try {
if (!resp.getRecord) {
throw new Error(`Invalid response format, expected record availability: ${JSON.stringify(resp)}`);
throw new SDKClientError(`Invalid response format, expected record availability: ${JSON.stringify(resp)}`);
}

const transactionRecord: TransactionRecord = await resp.getRecord(this.clientMain);
Expand All @@ -321,11 +313,9 @@ export class SDKClient {
e.status,
0,
callerName);

throw new Error(e.message);
}

throw e;
throw new SDKClientError(e.message, e?.status?._code);
}
};

Expand Down Expand Up @@ -353,7 +343,7 @@ export class SDKClient {

private static HbarToWeiBar(balance: AccountBalance): BigNumber {
return balance.hbars
.to(HbarUnit.Tinybar)
.multipliedBy(constants.TINYBAR_TO_WEIBAR_COEF);
.to(HbarUnit.Tinybar)
.multipliedBy(constants.TINYBAR_TO_WEIBAR_COEF);
}
}
26 changes: 26 additions & 0 deletions packages/relay/src/lib/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*
*/

import { Status } from "@hashgraph/sdk";

export class JsonRpcError {
public code: number;
public message: string;
Expand Down Expand Up @@ -107,3 +109,27 @@ export const predefined = {
message: 'Value below 10_000_000_000 wei which is 1 tinybar'
}),
};

export class SDKClientError extends Error {
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
public statusCode: number;

constructor(message: string, statusCode?: number) {
super(message);
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
this.statusCode = statusCode ? statusCode : Status.Unknown._code;
this.name = "SDKClientError";
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved

Object.setPrototypeOf(this, SDKClientError.prototype);
}
}

export class MirrorNodeClientError extends Error {
public statusCode: number;

constructor(message: string, statusCode: number) {
super(message);
this.statusCode = statusCode;
this.name = "MirrorNodeClientError";

Object.setPrototypeOf(this, MirrorNodeClientError.prototype);
}
}
59 changes: 32 additions & 27 deletions packages/relay/src/lib/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { BigNumber } from '@hashgraph/sdk/lib/Transfer';
import { Logger } from 'pino';
import { Block, Transaction, Log } from './model';
import { MirrorNodeClient, SDKClient } from './clients';
import { JsonRpcError, predefined } from './errors';
import { JsonRpcError, predefined, SDKClientError } from './errors';
import constants from './constants';
import { Precheck } from './precheck';

Expand Down Expand Up @@ -233,7 +233,7 @@ export class EthImpl implements Eth {
'transaction_type': EthImpl.ethTxType
}
]
};
};
}

if (networkFees && Array.isArray(networkFees.fees)) {
Expand Down Expand Up @@ -454,11 +454,13 @@ export class EthImpl implements Eth {

return EthImpl.numberTo0x(weibars);
} catch (e: any) {
// handle INVALID_ACCOUNT_ID
if (e?.status?._code === Status.InvalidAccountId._code) {
this.logger.debug(`Unable to find account ${account} in block ${JSON.stringify(blockNumber)}(${blockNumberOrTag}), returning 0x0 balance`);
cache.set(cachedLabel, EthImpl.zeroHex, constants.CACHE_TTL.ONE_HOUR);
return EthImpl.zeroHex;
if(e instanceof SDKClientError) {
// handle INVALID_ACCOUNT_ID
if (e.statusCode === Status.InvalidAccountId._code) {
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
this.logger.debug(`Unable to find account ${account} in block ${JSON.stringify(blockNumber)}(${blockNumberOrTag}), returning 0x0 balance`);
cache.set(cachedLabel, EthImpl.zeroHex, constants.CACHE_TTL.ONE_HOUR);
return EthImpl.zeroHex;
}
}

this.logger.error(e, 'Error raised during getBalance for account %s', account);
Expand Down Expand Up @@ -486,14 +488,18 @@ export class EthImpl implements Eth {
const bytecode = await this.sdkClient.getContractByteCode(0, 0, address, EthImpl.ethGetCode);
return EthImpl.prepend0x(Buffer.from(bytecode).toString('hex'));
} catch (e: any) {
// handle INVALID_CONTRACT_ID
if (e?.status?._code === Status.InvalidContractId._code || e?.message?.includes(Status.InvalidContractId.toString())) {
this.logger.debug('Unable to find code for contract %s in block "%s", returning 0x0, err code: %s', address, blockNumber, e?.status?._code);
cache.set(cachedLabel, '0x0', constants.CACHE_TTL.ONE_HOUR);
return '0x0';
if(e instanceof SDKClientError) {
// handle INVALID_CONTRACT_ID
if (e.statusCode === Status.InvalidContractId._code || e?.message?.includes(Status.InvalidContractId.toString())) {
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
this.logger.debug('Unable to find code for contract %s in block "%s", returning 0x0, err code: %s', address, blockNumber, e.statusCode);
cache.set(cachedLabel, '0x0', constants.CACHE_TTL.ONE_HOUR);
return '0x0';
}
this.logger.error(e, 'Error raised during getCode for address %s, err code: %s', address, e.statusCode);
} else {
this.logger.error(e, 'Error raised during getCode for address %s', address);
}

this.logger.error(e, 'Error raised during getCode for address %s, err code: %s', address, e?.status?._code);
throw e;
}
}
Expand Down Expand Up @@ -620,13 +626,18 @@ export class EthImpl implements Eth {
if (blockNumber === 0) {
return '0x0';
} else {
const result = await this.mirrorNodeClient.resolveEntityType(address);
if (result?.type === constants.TYPE_ACCOUNT) {
const accountInfo = await this.sdkClient.getAccountInfo(result?.entity.account, EthImpl.ethGetTransactionCount);
return EthImpl.numberTo0x(Number(accountInfo.ethereumNonce));
}
else if (result?.type === constants.TYPE_CONTRACT) {
return EthImpl.numberTo0x(1);
try {
const result = await this.mirrorNodeClient.resolveEntityType(address);
if (result?.type === constants.TYPE_ACCOUNT) {
const accountInfo = await this.sdkClient.getAccountInfo(result?.entity.account, EthImpl.ethGetTransactionCount);
return EthImpl.numberTo0x(Number(accountInfo.ethereumNonce));
}
else if (result?.type === constants.TYPE_CONTRACT) {
return EthImpl.numberTo0x(1);
}
} catch (e: any) {
this.logger.error(e, 'Error raised during getTransactionCount for address %s, block number or tag %s', address, blockNumOrTag);
throw e;
lukelee-sl marked this conversation as resolved.
Show resolved Hide resolved
}

return EthImpl.numberTo0x(0);
Expand Down Expand Up @@ -711,13 +722,7 @@ export class EthImpl implements Eth {
// FIXME Is this right? Maybe so?
return EthImpl.prepend0x(Buffer.from(contractCallResponse.asBytes()).toString('hex'));
} catch (e: any) {
// handle client error
let resolvedError = e;
if (e.status && e.status._code) {
resolvedError = new Error(e.message);
}

this.logger.error(resolvedError, 'Failed to successfully submit contractCallQuery');
this.logger.error(e, 'Failed to successfully submit contractCallQuery');
return predefined.INTERNAL_ERROR;
}
}
Expand Down
14 changes: 4 additions & 10 deletions packages/relay/tests/lib/eth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ import pino from 'pino';
import { Block, Transaction } from '../../src/lib/model';
import constants from '../../src/lib/constants';
import { SDKClient } from '../../src/lib/clients';
import { SDKClientError } from '../../src/lib/errors';

const logger = pino();
const registry = new Registry();
const Relay = new RelayImpl(logger, registry);
Expand Down Expand Up @@ -902,11 +904,7 @@ describe('Eth calls using MirrorNode', async function () {
mock.onGet(`accounts/${contractAddress1}`).reply(200, {
account: contractAddress1
});
sdkClientStub.getAccountBalanceInWeiBar.throws({
status: {
_code: 15
}
});
sdkClientStub.getAccountBalanceInWeiBar.throws(new SDKClientError("eth_getBalance exception", 15));

const resNoCache = await ethImpl.getBalance(contractAddress1, null);
const resCached = await ethImpl.getBalance(contractAddress1, null);
Expand Down Expand Up @@ -949,11 +947,7 @@ describe('Eth calls using MirrorNode', async function () {

describe('eth_getCode', async function() {
it('should return cached value', async () => {
sdkClientStub.getContractByteCode.throws({
status: {
_code: 16
}
});
sdkClientStub.getContractByteCode.throws(new SDKClientError("eth_getBalance exception", 16));

const resNoCache = await ethImpl.getCode(contractAddress1, null);
const resCached = await ethImpl.getCode(contractAddress1, null);
Expand Down