From e939481046f47d7574bb838093966df124afc508 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Mon, 25 Jul 2022 15:10:54 -0700 Subject: [PATCH 1/8] add specific error classes for SDKClient and MirrorNodeCLient Signed-off-by: lukelee-sl --- .../relay/src/lib/clients/mirrorNodeClient.ts | 6 +- packages/relay/src/lib/clients/sdkClient.ts | 28 +++---- packages/relay/src/lib/errors.ts | 26 +++++++ packages/relay/src/lib/eth.ts | 73 ++++++++++--------- packages/relay/tests/lib/eth.spec.ts | 14 +--- 5 files changed, 81 insertions(+), 66 deletions(-) diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index 2ed66ffa6..43bf04d71 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -19,7 +19,7 @@ */ import Axios, { AxiosInstance } from 'axios'; -import { predefined } from '../errors'; +import { MirrorNodeClientError, predefined } from '../errors'; import { Logger } from "pino"; import constants from './../constants'; import { Histogram, Registry } from 'prom-client'; @@ -151,7 +151,7 @@ export class MirrorNodeClient { return response.data; } catch (error: any) { ms = Date.now() - start; - const effectiveStatusCode = error.response !== undefined ? error.response.status : MirrorNodeClient.unknownServerErrorHttpStatusCode; + const effectiveStatusCode = error.response !== undefined ? error.response.status : MirrorNodeClient.unknownServerErrorHttpStatusCode; this.mirrorResponseHistogram.labels(pathLabel, effectiveStatusCode).observe(ms); this.handleError(error, path, effectiveStatusCode, allowedErrorStatuses); } @@ -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 { diff --git a/packages/relay/src/lib/clients/sdkClient.ts b/packages/relay/src/lib/clients/sdkClient.ts index eab04a43a..2190d36a7 100644 --- a/packages/relay/src/lib/clients/sdkClient.ts +++ b/packages/relay/src/lib/clients/sdkClient.ts @@ -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'); @@ -165,7 +166,7 @@ export class SDKClient { async getTinyBarGasFee(callerName: string): Promise { 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) { @@ -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 { @@ -257,11 +258,7 @@ export class SDKClient { query._queryPayment?.toTinybars().toNumber(), callerName); - if (e.status && e.status._code) { - throw new Error(e.message); - } - - throw e; + throw new SDKClientError(e.message, e?.status?._code); } }; @@ -283,19 +280,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 => { 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); @@ -317,11 +309,9 @@ export class SDKClient { e.status, 0, callerName); - - throw new Error(e.message); } - throw e; + throw new SDKClientError(e.message, e?.status?._code); } }; @@ -349,7 +339,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); } } diff --git a/packages/relay/src/lib/errors.ts b/packages/relay/src/lib/errors.ts index b4bdd548e..5769f0a8d 100644 --- a/packages/relay/src/lib/errors.ts +++ b/packages/relay/src/lib/errors.ts @@ -18,6 +18,8 @@ * */ +import { Status } from "@hashgraph/sdk"; + export class JsonRpcError { public code: number; public message: string; @@ -107,3 +109,27 @@ export const predefined = { message: 'Value below 10_000_000_000 wei which is 1 tinybar' }), }; + +export class SDKClientError extends Error { + public statusCode: number; + + constructor(message: string, statusCode?: number) { + super(message); + this.statusCode = statusCode ? statusCode : Status.Unknown._code; + this.name = "SDKClientError"; + + 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); + } +} \ No newline at end of file diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index 5a51a7062..55521277e 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -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'; @@ -226,14 +226,14 @@ export class EthImpl implements Eth { } if (_.isNil(networkFees)) { - networkFees = { - fees: [ - { - gas: await this.sdkClient.getTinyBarGasFee(callerName), - 'transaction_type': EthImpl.ethTxType - } - ] - }; + networkFees = { + fees: [ + { + gas: await this.sdkClient.getTinyBarGasFee(callerName), + 'transaction_type': EthImpl.ethTxType + } + ] + }; } if (networkFees && Array.isArray(networkFees.fees)) { @@ -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) { + 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); @@ -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())) { + 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; } } @@ -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; } return EthImpl.numberTo0x(0); @@ -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; } } diff --git a/packages/relay/tests/lib/eth.spec.ts b/packages/relay/tests/lib/eth.spec.ts index b853a46db..170f440f4 100644 --- a/packages/relay/tests/lib/eth.spec.ts +++ b/packages/relay/tests/lib/eth.spec.ts @@ -37,6 +37,8 @@ import { Block, Transaction } from '../../src/lib/model'; import constants from '../../src/lib/constants'; import { SDKClient } from '../../src/lib/clients'; import { TextEncoder } from 'util'; +import { SDKClientError } from '../../src/lib/errors'; + const logger = pino(); const registry = new Registry(); const Relay = new RelayImpl(logger, registry); @@ -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); @@ -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); From e5b8487dd31936bbe500de171c909ca3174078c0 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Mon, 25 Jul 2022 15:17:37 -0700 Subject: [PATCH 2/8] address sonarCloud issue Signed-off-by: lukelee-sl --- packages/relay/src/lib/clients/mirrorNodeClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index 43bf04d71..244f4ef73 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -19,7 +19,7 @@ */ import Axios, { AxiosInstance } from 'axios'; -import { MirrorNodeClientError, predefined } from '../errors'; +import { MirrorNodeClientError } from '../errors'; import { Logger } from "pino"; import constants from './../constants'; import { Histogram, Registry } from 'prom-client'; From dd60d5b44114a121c382cc39948754f6d9cab3eb Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Mon, 25 Jul 2022 17:19:18 -0700 Subject: [PATCH 3/8] fix spacing Signed-off-by: lukelee-sl --- packages/relay/src/lib/clients/mirrorNodeClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index 244f4ef73..a9dd84e63 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -151,7 +151,7 @@ export class MirrorNodeClient { return response.data; } catch (error: any) { ms = Date.now() - start; - const effectiveStatusCode = error.response !== undefined ? error.response.status : MirrorNodeClient.unknownServerErrorHttpStatusCode; + const effectiveStatusCode = error.response !== undefined ? error.response.status : MirrorNodeClient.unknownServerErrorHttpStatusCode; this.mirrorResponseHistogram.labels(pathLabel, effectiveStatusCode).observe(ms); this.handleError(error, path, effectiveStatusCode, allowedErrorStatuses); } From fb28bd339fb3e0845d6f685bcbff4f9b513b3de8 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Mon, 25 Jul 2022 17:22:12 -0700 Subject: [PATCH 4/8] revert spacing change Signed-off-by: lukelee-sl --- packages/relay/src/lib/eth.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index a3396d407..642e70e4e 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -226,14 +226,14 @@ export class EthImpl implements Eth { } if (_.isNil(networkFees)) { - networkFees = { - fees: [ - { - gas: await this.sdkClient.getTinyBarGasFee(callerName), - 'transaction_type': EthImpl.ethTxType - } - ] - }; + networkFees = { + fees: [ + { + gas: await this.sdkClient.getTinyBarGasFee(callerName), + 'transaction_type': EthImpl.ethTxType + } + ] + }; } if (networkFees && Array.isArray(networkFees.fees)) { From eea00817930b85e429b4eabae014c2892276d3b8 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Tue, 26 Jul 2022 10:54:36 -0700 Subject: [PATCH 5/8] refactor to address code review comments Signed-off-by: lukelee-sl --- packages/relay/src/index.ts | 4 +- .../relay/src/lib/clients/mirrorNodeClient.ts | 2 +- packages/relay/src/lib/clients/sdkClient.ts | 55 ++++++++++--------- .../lib/{errors.ts => errors/JsonRpcError.ts} | 28 +--------- .../src/lib/errors/MirrorNodeClientError.ts | 31 +++++++++++ .../relay/src/lib/errors/SDKClientError.ts | 55 +++++++++++++++++++ packages/relay/src/lib/eth.ts | 9 +-- packages/relay/src/lib/precheck.ts | 2 +- packages/relay/tests/lib/eth.spec.ts | 11 +++- packages/server/tests/acceptance/rpc.spec.ts | 2 +- packages/server/tests/clients/relayClient.ts | 2 +- packages/server/tests/helpers/assertions.ts | 2 +- 12 files changed, 137 insertions(+), 66 deletions(-) rename packages/relay/src/lib/{errors.ts => errors/JsonRpcError.ts} (82%) create mode 100644 packages/relay/src/lib/errors/MirrorNodeClientError.ts create mode 100644 packages/relay/src/lib/errors/SDKClientError.ts diff --git a/packages/relay/src/index.ts b/packages/relay/src/index.ts index 708928e6b..532ff8d3b 100644 --- a/packages/relay/src/index.ts +++ b/packages/relay/src/index.ts @@ -18,8 +18,8 @@ * */ -import {Block, Log, Receipt, Transaction} from './lib/model'; -import {JsonRpcError} from './lib/errors'; +import { Block, Log, Receipt, Transaction } from './lib/model'; +import { JsonRpcError } from './lib/errors/JsonRpcError'; export { JsonRpcError }; diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index a9dd84e63..85411f6c9 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -19,7 +19,7 @@ */ import Axios, { AxiosInstance } from 'axios'; -import { MirrorNodeClientError } from '../errors'; +import { MirrorNodeClientError } from './../errors/MirrorNodeClientError'; import { Logger } from "pino"; import constants from './../constants'; import { Histogram, Registry } from 'prom-client'; diff --git a/packages/relay/src/lib/clients/sdkClient.ts b/packages/relay/src/lib/clients/sdkClient.ts index 82f4f3012..388fa5f7a 100644 --- a/packages/relay/src/lib/clients/sdkClient.ts +++ b/packages/relay/src/lib/clients/sdkClient.ts @@ -45,7 +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'; +import { SDKClientError } from './../errors/SDKClientError'; const _ = require('lodash'); @@ -166,7 +166,7 @@ export class SDKClient { async getTinyBarGasFee(callerName: string): Promise { const feeSchedules = await this.getFeeSchedule(callerName); if (_.isNil(feeSchedules.current) || feeSchedules.current?.transactionFeeSchedule === undefined) { - throw new SDKClientError('Invalid FeeSchedules proto format'); + throw new SDKClientError({}, 'Invalid FeeSchedules proto format'); } for (const schedule of feeSchedules.current?.transactionFeeSchedule) { @@ -178,7 +178,7 @@ export class SDKClient { } } - throw new SDKClientError(`${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 { @@ -253,16 +253,18 @@ export class SDKClient { return resp; } catch (e: any) { - const statusCode = e.status ? e.status._code : Status.Unknown._code; - this.logger.debug(`Consensus Node query response: ${query.constructor.name} ${statusCode}`); - this.captureMetrics( - SDKClient.queryMode, - query.constructor.name, - e.status, - query._queryPayment?.toTinybars().toNumber(), - callerName); + const sdkClientError = new SDKClientError(e); + if(sdkClientError.isValidNetworkError()) { + this.logger.debug(`Consensus Node query response: ${query.constructor.name} ${sdkClientError.statusCode}`); + this.captureMetrics( + SDKClient.queryMode, + query.constructor.name, + sdkClientError.status, + query._queryPayment?.toTinybars().toNumber(), + callerName); + } - throw new SDKClientError(e.message, e?.status?._code); + throw sdkClientError; } }; @@ -275,23 +277,25 @@ export class SDKClient { return resp; } catch (e: any) { - const statusCode = e.status ? e.status._code : Status.Unknown._code; - this.logger.info(`Consensus Node ${transactionType} transaction response: ${statusCode}`); - this.captureMetrics( - SDKClient.transactionMode, - transactionType, - statusCode, - 0, - callerName); + const sdkClientError = new SDKClientError(e); + if(sdkClientError.isValidNetworkError()) { + this.logger.info(`Consensus Node ${transactionType} transaction response: ${sdkClientError.statusCode}`); + this.captureMetrics( + SDKClient.transactionMode, + transactionType, + sdkClientError.statusCode, + 0, + callerName); + } - throw new SDKClientError(e.message, e?.status?._code); + throw sdkClientError; } }; executeGetTransactionRecord = async (resp: TransactionResponse, transactionName: string, callerName: string): Promise => { try { if (!resp.getRecord) { - throw new SDKClientError(`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); @@ -306,16 +310,17 @@ export class SDKClient { } catch (e: any) { // capture sdk record retrieval errors and shorten familiar stack trace - if (e.status && e.status._code) { + const sdkClientError = new SDKClientError(e); + if(sdkClientError.isValidNetworkError()) { this.captureMetrics( SDKClient.transactionMode, transactionName, - e.status, + sdkClientError.status, 0, callerName); } - throw new SDKClientError(e.message, e?.status?._code); + throw sdkClientError; } }; diff --git a/packages/relay/src/lib/errors.ts b/packages/relay/src/lib/errors/JsonRpcError.ts similarity index 82% rename from packages/relay/src/lib/errors.ts rename to packages/relay/src/lib/errors/JsonRpcError.ts index 5769f0a8d..193cdad6d 100644 --- a/packages/relay/src/lib/errors.ts +++ b/packages/relay/src/lib/errors/JsonRpcError.ts @@ -18,8 +18,6 @@ * */ -import { Status } from "@hashgraph/sdk"; - export class JsonRpcError { public code: number; public message: string; @@ -108,28 +106,4 @@ export const predefined = { code: -32602, message: 'Value below 10_000_000_000 wei which is 1 tinybar' }), -}; - -export class SDKClientError extends Error { - public statusCode: number; - - constructor(message: string, statusCode?: number) { - super(message); - this.statusCode = statusCode ? statusCode : Status.Unknown._code; - this.name = "SDKClientError"; - - 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); - } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/packages/relay/src/lib/errors/MirrorNodeClientError.ts b/packages/relay/src/lib/errors/MirrorNodeClientError.ts new file mode 100644 index 000000000..36e2258d8 --- /dev/null +++ b/packages/relay/src/lib/errors/MirrorNodeClientError.ts @@ -0,0 +1,31 @@ + +/*- + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +export class MirrorNodeClientError extends Error { + public statusCode: number; + + constructor(message: string, statusCode: number) { + super(message); + this.statusCode = statusCode; + + Object.setPrototypeOf(this, MirrorNodeClientError.prototype); + } + } \ No newline at end of file diff --git a/packages/relay/src/lib/errors/SDKClientError.ts b/packages/relay/src/lib/errors/SDKClientError.ts new file mode 100644 index 000000000..35138195e --- /dev/null +++ b/packages/relay/src/lib/errors/SDKClientError.ts @@ -0,0 +1,55 @@ +/*- + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2022 Hedera Hashgraph, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { Status } from "@hashgraph/sdk"; + +export class SDKClientError extends Error { + public status: Status = Status.Unknown; + private validNetworkError: boolean = false; + + constructor(e: any, message?: string) { + super(e && e.status && e.status._code ? e.message : message); + + if(e && e.status && e.status._code) { + this.validNetworkError = true; + this.status = e.status; + } + + Object.setPrototypeOf(this, SDKClientError.prototype); + } + + get statusCode(): number { + return this.status._code; + } + + public isValidNetworkError(): boolean { + return this.validNetworkError; + } + + public isInvalidAccountId(): boolean { + return this.isValidNetworkError() && this.statusCode === Status.InvalidAccountId._code; + } + + public isInvalidContractId(): boolean { + return this.isValidNetworkError() && + (this.statusCode === Status.InvalidContractId._code || this.message?.includes(Status.InvalidContractId.toString())); + } +} + \ No newline at end of file diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index 642e70e4e..669b596b6 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -24,7 +24,8 @@ 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, SDKClientError } from './errors'; +import { JsonRpcError, predefined } from './errors/JsonRpcError'; +import { SDKClientError } from './errors/SDKClientError'; import constants from './constants'; import { Precheck } from './precheck'; @@ -233,7 +234,7 @@ export class EthImpl implements Eth { 'transaction_type': EthImpl.ethTxType } ] - }; + }; } if (networkFees && Array.isArray(networkFees.fees)) { @@ -456,7 +457,7 @@ export class EthImpl implements Eth { } catch (e: any) { if(e instanceof SDKClientError) { // handle INVALID_ACCOUNT_ID - if (e.statusCode === Status.InvalidAccountId._code) { + if (e.isInvalidAccountId()) { 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; @@ -490,7 +491,7 @@ export class EthImpl implements Eth { } catch (e: any) { if(e instanceof SDKClientError) { // handle INVALID_CONTRACT_ID - if (e.statusCode === Status.InvalidContractId._code || e?.message?.includes(Status.InvalidContractId.toString())) { + if (e.isInvalidContractId()) { 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'; diff --git a/packages/relay/src/lib/precheck.ts b/packages/relay/src/lib/precheck.ts index 834b27397..dcf0279b4 100644 --- a/packages/relay/src/lib/precheck.ts +++ b/packages/relay/src/lib/precheck.ts @@ -19,7 +19,7 @@ */ import * as ethers from 'ethers'; -import { predefined } from './errors'; +import { predefined } from './errors/JsonRpcError'; import { MirrorNodeClient, SDKClient } from './clients'; import { EthImpl } from './eth'; import { Logger } from 'pino'; diff --git a/packages/relay/tests/lib/eth.spec.ts b/packages/relay/tests/lib/eth.spec.ts index 103389ba7..990cb37fb 100644 --- a/packages/relay/tests/lib/eth.spec.ts +++ b/packages/relay/tests/lib/eth.spec.ts @@ -36,7 +36,7 @@ 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'; +import { SDKClientError } from '../../src/lib/errors/SDKClientError'; const logger = pino(); const registry = new Registry(); @@ -904,7 +904,10 @@ describe('Eth calls using MirrorNode', async function () { mock.onGet(`accounts/${contractAddress1}`).reply(200, { account: contractAddress1 }); - sdkClientStub.getAccountBalanceInWeiBar.throws(new SDKClientError("eth_getBalance exception", 15)); + sdkClientStub.getAccountBalanceInWeiBar.throws(new SDKClientError( + {status: { + _code: 15 + }})); const resNoCache = await ethImpl.getBalance(contractAddress1, null); const resCached = await ethImpl.getBalance(contractAddress1, null); @@ -947,7 +950,9 @@ describe('Eth calls using MirrorNode', async function () { describe('eth_getCode', async function() { it('should return cached value', async () => { - sdkClientStub.getContractByteCode.throws(new SDKClientError("eth_getBalance exception", 16)); + sdkClientStub.getContractByteCode.throws(new SDKClientError({status: { + _code: 16 + }})); const resNoCache = await ethImpl.getCode(contractAddress1, null); const resCached = await ethImpl.getCode(contractAddress1, null); diff --git a/packages/server/tests/acceptance/rpc.spec.ts b/packages/server/tests/acceptance/rpc.spec.ts index b48318d31..a084c7021 100644 --- a/packages/server/tests/acceptance/rpc.spec.ts +++ b/packages/server/tests/acceptance/rpc.spec.ts @@ -30,7 +30,7 @@ import { AccountBalanceQuery, ContractFunctionParameters } from '@hashgraph/sdk' import parentContractJson from '../contracts/Parent.json'; import basicContractJson from '../contracts/Basic.json'; import logsContractJson from '../contracts/Logs.json'; -import { predefined } from '../../../relay/src/lib/errors'; +import { predefined } from '../../../relay/src/lib/errors/JsonRpcError'; describe('RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds diff --git a/packages/server/tests/clients/relayClient.ts b/packages/server/tests/clients/relayClient.ts index c1a1fae74..29835c741 100644 --- a/packages/server/tests/clients/relayClient.ts +++ b/packages/server/tests/clients/relayClient.ts @@ -21,7 +21,7 @@ import { ethers, providers } from 'ethers'; import { Logger } from 'pino'; import Assertions from '../helpers/assertions'; -import { predefined } from '../../../relay/src/lib/errors'; +import { predefined } from '../../../relay/src/lib/errors/JsonRpcError'; export default class RelayClient { diff --git a/packages/server/tests/helpers/assertions.ts b/packages/server/tests/helpers/assertions.ts index 2ec4100ba..089266a8b 100644 --- a/packages/server/tests/helpers/assertions.ts +++ b/packages/server/tests/helpers/assertions.ts @@ -19,7 +19,7 @@ */ import { expect } from 'chai'; import { ethers, BigNumber } from 'ethers'; -import { JsonRpcError, predefined } from '../../../relay/src/lib/errors'; +import { JsonRpcError, predefined } from '../../../relay/src/lib/errors/JsonRpcError'; import { Utils } from './utils'; export default class Assertions { From d7e49a1889fa9dbfec31354ff96972df553d8a11 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Tue, 26 Jul 2022 10:57:44 -0700 Subject: [PATCH 6/8] address sonarCloud issue Signed-off-by: lukelee-sl --- packages/relay/src/lib/eth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index 669b596b6..a71517d1d 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -19,7 +19,7 @@ */ import { Eth } from '../index'; -import { ContractId, Status, Hbar, EthereumTransaction } from '@hashgraph/sdk'; +import { ContractId, Hbar, EthereumTransaction } from '@hashgraph/sdk'; import { BigNumber } from '@hashgraph/sdk/lib/Transfer'; import { Logger } from 'pino'; import { Block, Transaction, Log } from './model'; From 0354278a2a54be97a6b6eacf81d25d81680ce9f2 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Tue, 26 Jul 2022 15:05:10 -0700 Subject: [PATCH 7/8] address code review comments Signed-off-by: lukelee-sl --- packages/relay/src/lib/errors/JsonRpcError.ts | 2 +- packages/relay/src/lib/errors/MirrorNodeClientError.ts | 3 ++- packages/relay/src/lib/eth.ts | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/relay/src/lib/errors/JsonRpcError.ts b/packages/relay/src/lib/errors/JsonRpcError.ts index 193cdad6d..b4bdd548e 100644 --- a/packages/relay/src/lib/errors/JsonRpcError.ts +++ b/packages/relay/src/lib/errors/JsonRpcError.ts @@ -106,4 +106,4 @@ export const predefined = { code: -32602, message: 'Value below 10_000_000_000 wei which is 1 tinybar' }), -}; \ No newline at end of file +}; diff --git a/packages/relay/src/lib/errors/MirrorNodeClientError.ts b/packages/relay/src/lib/errors/MirrorNodeClientError.ts index 36e2258d8..887819d2b 100644 --- a/packages/relay/src/lib/errors/MirrorNodeClientError.ts +++ b/packages/relay/src/lib/errors/MirrorNodeClientError.ts @@ -28,4 +28,5 @@ export class MirrorNodeClientError extends Error { Object.setPrototypeOf(this, MirrorNodeClientError.prototype); } - } \ No newline at end of file + } + \ No newline at end of file diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index a71517d1d..48c8bcbca 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -621,7 +621,7 @@ export class EthImpl implements Eth { * @param address * @param blockNumOrTag */ - async getTransactionCount(address: string, blockNumOrTag: string): Promise { + async getTransactionCount(address: string, blockNumOrTag: string): Promise { this.logger.trace('getTransactionCount(address=%s, blockNumOrTag=%s)', address, blockNumOrTag); const blockNumber = await this.translateBlockTag(blockNumOrTag); if (blockNumber === 0) { @@ -638,7 +638,7 @@ export class EthImpl implements Eth { } } catch (e: any) { this.logger.error(e, 'Error raised during getTransactionCount for address %s, block number or tag %s', address, blockNumOrTag); - throw e; + return predefined.INTERNAL_ERROR; } return EthImpl.numberTo0x(0); From 79716e4c72823db6e49f7565ab21e540112ff105 Mon Sep 17 00:00:00 2001 From: lukelee-sl Date: Tue, 26 Jul 2022 15:10:05 -0700 Subject: [PATCH 8/8] fix build error Signed-off-by: lukelee-sl --- packages/relay/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/relay/src/index.ts b/packages/relay/src/index.ts index 532ff8d3b..6d01fc6cb 100644 --- a/packages/relay/src/index.ts +++ b/packages/relay/src/index.ts @@ -79,7 +79,7 @@ export interface Eth { getTransactionByHash(hash: string): Promise; - getTransactionCount(address: string, blocknum: string): Promise; + getTransactionCount(address: string, blocknum: string): Promise; getTransactionReceipt(hash: string): Promise;