diff --git a/.env.http.example b/.env.http.example index 03ff988f9f..8ed0f701d8 100644 --- a/.env.http.example +++ b/.env.http.example @@ -32,7 +32,6 @@ OPERATOR_KEY_MAIN= # Operator private key used to sign transaction # ========== ETH CALL CONFIGURATION ========== # CONTRACT_CALL_GAS_LIMIT=50000000 # Maximum gas limit applied to eth_call endpoint # ETH_CALL_CACHE_TTL=200 # Maximum time in ms to cache an eth_call response -# ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=false # If true, eth_call first queries consensus node instead of mirror # ETH_CALL_ACCEPTED_ERRORS=[] # List of acceptable error codes for eth_call requests for retry # ========== FEE & GAS CONFIGURATION ========== diff --git a/.env.ws.example b/.env.ws.example index 2fa1f0451e..98b580d52b 100644 --- a/.env.ws.example +++ b/.env.ws.example @@ -46,7 +46,6 @@ SUBSCRIPTIONS_ENABLED=true # Must be true for the WebSocket server to func # MIRROR_NODE_AGENT_CACHEABLE_DNS=true # Enable DNS caching for mirror node # ========== ETH CALL CONFIGURATION ========== -# ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=false # If true, eth_call first queries consensus node instead of mirror # CONSENSUS_MAX_EXECUTION_TIME=15000 # Max time in ms before TIMEOUT error # SDK_REQUEST_TIMEOUT=10000 # Timeout for SDK execute() method # CONTRACT_QUERY_TIMEOUT_RETRIES=3 # Maximum retries for failed contract call queries diff --git a/charts/hedera-json-rpc-relay-websocket/values.yaml b/charts/hedera-json-rpc-relay-websocket/values.yaml index 8def43f5f5..ede923c22a 100644 --- a/charts/hedera-json-rpc-relay-websocket/values.yaml +++ b/charts/hedera-json-rpc-relay-websocket/values.yaml @@ -55,7 +55,6 @@ config: # MIRROR_NODE_AGENT_CACHEABLE_DNS: # ========== ETH CALL CONFIGURATION ========== - # ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: # CONSENSUS_MAX_EXECUTION_TIME: # SDK_REQUEST_TIMEOUT: # CONTRACT_QUERY_TIMEOUT_RETRIES: diff --git a/charts/hedera-json-rpc-relay/environments/minikube.yaml b/charts/hedera-json-rpc-relay/environments/minikube.yaml index e68439528c..9100874edf 100644 --- a/charts/hedera-json-rpc-relay/environments/minikube.yaml +++ b/charts/hedera-json-rpc-relay/environments/minikube.yaml @@ -10,8 +10,6 @@ config: HEDERA_NETWORK: {"127.0.01:50211":"0.0.3"} OPERATOR_ID_MAIN: "" OPERATOR_KEY_MAIN: "" - OPERATOR_ID_ETH_SENDRAWTRANSACTION: "" - OPERATOR_KEY_ETH_SENDRAWTRANSACTION: "" rolling_restart: enabled: true diff --git a/charts/hedera-json-rpc-relay/templates/secret.yaml b/charts/hedera-json-rpc-relay/templates/secret.yaml index 1fc878ad30..0383caf622 100644 --- a/charts/hedera-json-rpc-relay/templates/secret.yaml +++ b/charts/hedera-json-rpc-relay/templates/secret.yaml @@ -8,5 +8,3 @@ type: Opaque stringData: OPERATOR_ID_MAIN: {{ .Values.config.OPERATOR_ID_MAIN | quote }} OPERATOR_KEY_MAIN: {{ .Values.config.OPERATOR_KEY_MAIN | quote }} - OPERATOR_ID_ETH_SENDRAWTRANSACTION: {{ .Values.config.OPERATOR_ID_ETH_SENDRAWTRANSACTION | default (printf "%q" "") }} - OPERATOR_KEY_ETH_SENDRAWTRANSACTION: {{ .Values.config.OPERATOR_KEY_ETH_SENDRAWTRANSACTION | default (printf "%q" "") }} diff --git a/charts/hedera-json-rpc-relay/values.yaml b/charts/hedera-json-rpc-relay/values.yaml index 0673a70df3..191fb16026 100644 --- a/charts/hedera-json-rpc-relay/values.yaml +++ b/charts/hedera-json-rpc-relay/values.yaml @@ -42,7 +42,6 @@ config: # ========== ETH CALL CONFIGURATION ========== # CONTRACT_CALL_GAS_LIMIT: # ETH_CALL_CACHE_TTL: - # ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: # ETH_CALL_ACCEPTED_ERRORS: # ========== FEE & GAS CONFIGURATION ========== diff --git a/docs/configuration.md b/docs/configuration.md index d30d264840..28fcb4e0db 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -41,7 +41,6 @@ Unless you need to set a non-default value, it is recommended to only populate o | `ETH_BLOCK_NUMBER_CACHE_TTL_MS` | "1000" | Time in ms to cache response from mirror node | | `ETH_CALL_ACCEPTED_ERRORS` | "[]" | A list of acceptable error codes for eth_call requests. If an error code in this list is returned, the request will be retried. | | `ETH_CALL_CACHE_TTL` | "200" | Maximum time in ms to cache an eth_call response. | -| `ETH_CALL_DEFAULT_TO_CONSENSUS_NODE` | "false" | Flag to set if eth_call logic should first query the mirror node. | | `ETH_FEE_HISTORY_FIXED` | "true" | Flag to set if eth_feeHistory should return a fixed fee for the set of results. | | `ETH_GET_BALANCE_CACHE_TTL_MS` | "1000" | Time in ms to cache balance returned | | `ETH_GET_BLOCK_BY_RESULTS_BATCH_SIZE` | "25" | The number of contract results to request from the Mirror Node per batch durin an eth_getBlockByHash or eth_getBlockByNumber call | diff --git a/docs/rpc-api.md b/docs/rpc-api.md index eb71ca014b..2f67728e7b 100644 --- a/docs/rpc-api.md +++ b/docs/rpc-api.md @@ -68,7 +68,7 @@ Below is a comprehensive table of all Ethereum JSON-RPC methods from the [Ethere | [eth_accounts](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_accounts) | **Implemented** - Returns `[]` | N/A | Always returns empty array per Infura behavior | | [eth_blobBaseFee](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blobBaseFee) | **Implemented** - Returns `-32601` (Method not supported) | N/A | EIP-4844 blob transactions not supported | | [eth_blockNumber](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber) | **Implemented** | Mirror Node | | -| [eth_call](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call) | **Implemented** | Mirror Node, Consensus Node (conditional) | Falls back to Consensus Node only if `ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=true` | +| [eth_call](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_call) | **Implemented** | Mirror Node | | | [eth_chainId](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_chainid) | **Implemented** | N/A | Returns configured chain ID from environment | | [eth_coinbase](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_coinbase) | **Implemented** - Returns `-32601` (Method not supported) | N/A | Fixed zero address as Hedera has no traditional coinbase | | [eth_createAccessList](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_createaccesslist) | **Not Implemented** - Returns `-32601` (Method not found) | N/A | | diff --git a/packages/config-service/src/services/globalConfig.ts b/packages/config-service/src/services/globalConfig.ts index 0d4904ca64..17ac26e746 100644 --- a/packages/config-service/src/services/globalConfig.ts +++ b/packages/config-service/src/services/globalConfig.ts @@ -214,12 +214,6 @@ const _CONFIG = { required: false, defaultValue: 200, }, - ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: { - envName: 'ETH_CALL_DEFAULT_TO_CONSENSUS_NODE', - type: 'boolean', - required: false, - defaultValue: false, - }, ETH_FEE_HISTORY_FIXED: { envName: 'ETH_FEE_HISTORY_FIXED', type: 'boolean', @@ -587,24 +581,12 @@ const _CONFIG = { required: false, defaultValue: true, }, - OPERATOR_ID_ETH_SENDRAWTRANSACTION: { - envName: 'OPERATOR_ID_ETH_SENDRAWTRANSACTION', - type: 'string', - required: false, - defaultValue: null, - }, OPERATOR_ID_MAIN: { envName: 'OPERATOR_ID_MAIN', type: 'string', required: false, defaultValue: null, }, - OPERATOR_KEY_ETH_SENDRAWTRANSACTION: { - envName: 'OPERATOR_KEY_ETH_SENDRAWTRANSACTION', - type: 'string', - required: false, - defaultValue: null, - }, OPERATOR_KEY_FORMAT: { envName: 'OPERATOR_KEY_FORMAT', type: 'string', diff --git a/packages/config-service/src/services/loggerService.ts b/packages/config-service/src/services/loggerService.ts index 605ff8ed5d..6c61e14ea4 100644 --- a/packages/config-service/src/services/loggerService.ts +++ b/packages/config-service/src/services/loggerService.ts @@ -5,7 +5,6 @@ import { GlobalConfig } from './globalConfig'; export class LoggerService { public static readonly SENSITIVE_FIELDS = [ GlobalConfig.ENTRIES.OPERATOR_KEY_MAIN.envName, - GlobalConfig.ENTRIES.OPERATOR_KEY_ETH_SENDRAWTRANSACTION.envName, GlobalConfig.ENTRIES.GITHUB_TOKEN.envName, GlobalConfig.ENTRIES.GH_ACCESS_TOKEN.envName, ]; diff --git a/packages/relay/src/formatters.ts b/packages/relay/src/formatters.ts index 2563568e6c..ec4ffd582c 100644 --- a/packages/relay/src/formatters.ts +++ b/packages/relay/src/formatters.ts @@ -253,11 +253,6 @@ const isHex = (value: string): boolean => { return hexRegex.test(value); }; -const getFunctionSelector = (data?: string): string => { - if (!data) return ''; - return data.replace(/^0x/, '').substring(0, 8); -}; - const tinybarsToWeibars = (value: number | null, allowNegativeValues: boolean = false) => { if (value && value < 0) { // negative amount can be received only by CONTRACT_NEGATIVE_VALUE revert @@ -301,7 +296,6 @@ export { isValidEthereumAddress, isHex, ASCIIToHex, - getFunctionSelector, mapKeysAndValues, tinybarsToWeibars, }; diff --git a/packages/relay/src/lib/services/ethService/contractService/ContractService.ts b/packages/relay/src/lib/services/ethService/contractService/ContractService.ts index 3977e2ab96..7bd7afc84a 100644 --- a/packages/relay/src/lib/services/ethService/contractService/ContractService.ts +++ b/packages/relay/src/lib/services/ethService/contractService/ContractService.ts @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 import { ConfigService } from '@hashgraph/json-rpc-config-service/dist/services'; -import crypto from 'crypto'; import { Logger } from 'pino'; import { @@ -12,12 +11,10 @@ import { trimPrecedingZeros, weibarHexToTinyBarInt, } from '../../../../formatters'; -import { getFunctionSelector } from '../../../../formatters'; import { MirrorNodeClient } from '../../../clients'; import constants from '../../../constants'; import { JsonRpcError, predefined } from '../../../errors/JsonRpcError'; import { MirrorNodeClientError } from '../../../errors/MirrorNodeClientError'; -import { SDKClientError } from '../../../errors/SDKClientError'; import { Log } from '../../../model'; import { Precheck } from '../../../precheck'; import { IContractCallRequest, IContractCallResponse, IGetLogsParams, RequestDetails } from '../../../types'; @@ -132,7 +129,7 @@ export class ContractService implements IContractService { const gas = this.getCappedBlockGasLimit(call.gas?.toString(), requestDetails); await this.contractCallFormat(call, requestDetails); - const result = await this.routeAndExecuteCall(call, gas, blockNumberOrTag, requestDetails); + const result = await this.callMirrorNode(call, gas, call.value, blockNumberOrTag, requestDetails); if (this.logger.isLevelEnabled('debug')) { this.logger.debug(`${requestDetails.formattedRequestId} eth_call response: ${JSON.stringify(result)}`); } @@ -333,73 +330,6 @@ export class ContractService implements IContractService { return result; } - /** - * Caches the response from a successful call. - * - * @param {IContractCallRequest} call - The original call request - * @param {string} response - The response to cache - * @param {RequestDetails} requestDetails - The request details - * @returns {Promise} - * @private - */ - private async cacheResponse( - call: IContractCallRequest, - response: string, - requestDetails: RequestDetails, - ): Promise { - const data = call.data - ? crypto - .createHash('sha1') - .update(call.data || '0x') - .digest('hex') - : null; // NOSONAR - const cacheKey = `${constants.CACHE_KEY.ETH_CALL}:${call.from || ''}.${call.to}.${data}`; - const ethCallCacheTtl = parseNumericEnvVar('ETH_CALL_CACHE_TTL', 'ETH_CALL_CACHE_TTL_DEFAULT'); - await this.cacheService.set(cacheKey, response, constants.ETH_CALL, requestDetails, ethCallCacheTtl); - } - - /** - * Execute a contract call query to the consensus node - * - * @param {IContractCallRequest} call - The call data - * @param {number | null} gas - The gas limit - * @param {RequestDetails} requestDetails - The request details for logging and tracking - * @returns {Promise} The call result or error - */ - private async callConsensusNode( - call: IContractCallRequest, - gas: number | null, - requestDetails: RequestDetails, - ): Promise { - const requestIdPrefix = requestDetails.formattedRequestId; - - try { - gas = gas ?? Number.parseInt(this.defaultGas); - - if (this.logger.isLevelEnabled('debug')) { - this.logger.debug( - `${requestIdPrefix} Making eth_call on contract ${call.to} with gas ${gas} and call data "${call.data}" from "${call.from}" using consensus-node.`, - call.to, - gas, - call.data, - call.from, - ); - } - - await this.validateAddresses(call); - const cachedResponse = await this.tryGetCachedResponse(call, requestDetails); - if (cachedResponse != undefined) { - if (this.logger.isLevelEnabled('debug')) { - this.logger.debug(`${requestIdPrefix} eth_call returned cached response: ${cachedResponse}`); - } - return cachedResponse; - } - return await this.executeConsensusNodeCall(call, gas, requestDetails); - } catch (e: any) { - return this.handleConsensusNodeError(e, requestDetails); - } - } - /** * Makes a contract call via the Mirror Node. * @@ -492,42 +422,6 @@ export class ContractService implements IContractService { return this.mirrorNodeClient.postContractCall(callData, requestDetails); } - /** - * Executes the consensus node call and handles the response. - * - * @param {IContractCallRequest} call - The call request - * @param {number} gas - The gas limit - * @param {RequestDetails} requestDetails - The request details - * @returns {Promise} The call result or error - * @private - */ - private async executeConsensusNodeCall( - call: IContractCallRequest, - gas: number, - requestDetails: RequestDetails, - ): Promise { - const contractCallResponse = await this.hapiService - .getSDKClient() - .submitContractCallQueryWithRetry( - call.to as string, - call.data as string, - gas, - call.from as string, - constants.ETH_CALL, - requestDetails, - ); - - if (!contractCallResponse) { - return predefined.INTERNAL_ERROR( - `Invalid contractCallResponse from consensus-node: ${JSON.stringify(contractCallResponse)}`, - ); - } - - const formattedCallResponse = prepend0x(Buffer.from(contractCallResponse.asBytes()).toString('hex')); - await this.cacheResponse(call, formattedCallResponse, requestDetails); - return formattedCallResponse; - } - /** * Executes the mirror node call and formats the response. * @@ -632,27 +526,6 @@ export class ContractService implements IContractService { return gas; } - /** - * Handles errors from consensus node calls. - * - * @param {any} e - The error to handle - * @param {RequestDetails} requestDetails - The request details - * @returns {string | JsonRpcError} The appropriate error response - * @private - */ - private handleConsensusNodeError(e: any, requestDetails: RequestDetails): string | JsonRpcError { - const requestIdPrefix = requestDetails.formattedRequestId; - this.logger.error(e, `${requestIdPrefix} Failed to successfully submit contractCallQuery`); - - if (e instanceof JsonRpcError) { - return e; - } - - if (e instanceof SDKClientError) { - this.hapiService.decrementErrorCounter(e.statusCode); - } - return predefined.INTERNAL_ERROR(e.message.toString()); - } /** * Handles specific mirror node client errors. * @@ -804,71 +677,4 @@ export class ContractService implements IContractService { ...(block !== null ? { block } : {}), }; } - - /** - * Routes the call to either consensus or mirror node based on configuration. - * - * @param {IContractCallRequest} call - The call request - * @param {number | null} gas - The gas limit - * @param {string | null} blockNumberOrTag - The block number or tag - * @param {RequestDetails} requestDetails - The request details - * @returns {Promise} The call result - * @private - */ - private async routeAndExecuteCall( - call: IContractCallRequest, - gas: number | null, - blockNumberOrTag: string | null, - requestDetails: RequestDetails, - ): Promise { - // ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = false enables the use of Mirror node - const shouldDefaultToConsensus = ConfigService.get('ETH_CALL_DEFAULT_TO_CONSENSUS_NODE'); - - if (shouldDefaultToConsensus) { - return await this.callConsensusNode(call, gas, requestDetails); - } - - return await this.callMirrorNode(call, gas, call.value, blockNumberOrTag, requestDetails); - } - - /** - * Attempts to retrieve a cached response for the call. - * - * @param {IContractCallRequest} call - The call request - * @param {RequestDetails} requestDetails - The request details - * @returns {Promise} The cached response if found - * @private - */ - private async tryGetCachedResponse( - call: IContractCallRequest, - requestDetails: RequestDetails, - ): Promise { - const data = call.data - ? crypto - .createHash('sha1') - .update(call.data || '0x') - .digest('hex') - : null; // NOSONAR - const cacheKey = `${constants.CACHE_KEY.ETH_CALL}:${call.from || ''}.${call.to}.${data}`; - const cachedResponse = await this.cacheService.getAsync(cacheKey, constants.ETH_CALL, requestDetails); - - return cachedResponse === null ? undefined : cachedResponse; - } - - /** - * Validates the from and to addresses in the call request. - * - * @param {IContractCallRequest} call - The call request to validate - * @returns {Promise} - * @private - */ - private async validateAddresses(call: IContractCallRequest): Promise { - if (call.from && !isValidEthereumAddress(call.from)) { - throw predefined.NON_EXISTING_ACCOUNT(call.from); - } - - if (!isValidEthereumAddress(call.to)) { - throw predefined.INVALID_CONTRACT_ADDRESS(call.to); - } - } } diff --git a/packages/relay/src/lib/services/hapiService/hapiService.ts b/packages/relay/src/lib/services/hapiService/hapiService.ts index 38e0256cc0..126a0cba97 100644 --- a/packages/relay/src/lib/services/hapiService/hapiService.ts +++ b/packages/relay/src/lib/services/hapiService/hapiService.ts @@ -271,10 +271,9 @@ export default class HAPIService { * Configure Client * @param {Logger} logger * @param {string} hederaNetwork - * @param {string | null} type * @returns Client */ - private initClient(logger: Logger, hederaNetwork: string, type: string | null = null): Client { + private initClient(logger: Logger, hederaNetwork: string): Client { let client: Client; if (hederaNetwork in constants.CHAIN_IDS) { client = Client.forName(hederaNetwork); @@ -282,7 +281,7 @@ export default class HAPIService { client = Client.forNetwork(JSON.parse(hederaNetwork)); } - const operator = Utils.getOperator(logger, type); + const operator = Utils.getOperator(logger); if (operator) { client.setOperator(operator.accountId, operator.privateKey); } diff --git a/packages/relay/src/utils.ts b/packages/relay/src/utils.ts index bfa33ed50a..16d9503506 100644 --- a/packages/relay/src/utils.ts +++ b/packages/relay/src/utils.ts @@ -124,20 +124,14 @@ export class Utils { /** * Gets operator credentials based on the provided type. * @param {Logger} logger - The logger instance - * @param {string | null} type - The type of operator (e.g. 'eth_sendRawTransaction') * @returns {Operator | null} The operator credentials or null if not found */ - public static getOperator(logger: Logger, type: string | null = null): Operator | null { - const [operatorId, operatorKey] = - type === 'eth_sendRawTransaction' - ? [ - ConfigService.get('OPERATOR_ID_ETH_SENDRAWTRANSACTION'), - ConfigService.get('OPERATOR_KEY_ETH_SENDRAWTRANSACTION'), - ] - : [ConfigService.get('OPERATOR_ID_MAIN'), ConfigService.get('OPERATOR_KEY_MAIN')]; + public static getOperator(logger: Logger): Operator | null { + const operatorId = ConfigService.get('OPERATOR_ID_MAIN'); + const operatorKey = ConfigService.get('OPERATOR_KEY_MAIN'); if (!operatorId || !operatorKey) { - logger.warn(`Invalid operatorId or operatorKey for ${type ?? 'main'} client.`); + logger.warn(`Invalid operatorId or operatorKey for main client.`); return null; } diff --git a/packages/relay/tests/lib/eth/eth_call.spec.ts b/packages/relay/tests/lib/eth/eth_call.spec.ts index 3461c725aa..3a5ea81501 100644 --- a/packages/relay/tests/lib/eth/eth_call.spec.ts +++ b/packages/relay/tests/lib/eth/eth_call.spec.ts @@ -84,15 +84,11 @@ describe('@ethCall Eth Call spec', async function () { }); describe('eth_call precheck failures', async function () { - let callConsensusNodeSpy: sinon.SinonSpy; let callMirrorNodeSpy: sinon.SinonSpy; let sandbox: sinon.SinonSandbox; - overrideEnvsInMochaDescribe({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: false }); - beforeEach(() => { sandbox = sinon.createSandbox(); - callConsensusNodeSpy = sandbox.spy(contractService, 'callConsensusNode'); callMirrorNodeSpy = sandbox.spy(contractService, 'callMirrorNode'); }); @@ -119,74 +115,24 @@ describe('@ethCall Eth Call spec', async function () { ); }); - withOverriddenEnvsInMochaTest({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: false }, () => { - it('should execute "eth_call" against mirror node with a false ETH_CALL_DEFAULT_TO_CONSENSUS_NODE', async function () { - web3Mock.onPost('contracts/call').reply(200); - restMock.onGet(`contracts/${defaultCallData.from}`).reply(404); - restMock.onGet(`accounts/${defaultCallData.from}${NO_TRANSACTIONS}`).reply( - 200, - JSON.stringify({ - account: '0.0.1723', - evm_address: defaultCallData.from, - }), - ); - restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, JSON.stringify(DEFAULT_CONTRACT)); - - await contractService.call( - { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, - 'latest', - requestDetails, - ); - assert(callMirrorNodeSpy.calledOnce); - assert(callConsensusNodeSpy.notCalled); - }); - }); - - withOverriddenEnvsInMochaTest({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: undefined }, () => { - it('should execute "eth_call" against mirror node with an undefined ETH_CALL_DEFAULT_TO_CONSENSUS_NODE', async function () { - web3Mock.onPost('contracts/call').reply(200); - restMock.onGet(`contracts/${defaultCallData.from}`).reply(404); - restMock.onGet(`accounts/${defaultCallData.from}${NO_TRANSACTIONS}`).reply( - 200, - JSON.stringify({ - account: '0.0.1723', - evm_address: defaultCallData.from, - }), - ); - restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, JSON.stringify(DEFAULT_CONTRACT)); - - await contractService.call( - { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, - 'latest', - requestDetails, - ); - - assert(callMirrorNodeSpy.calledOnce); - assert(callConsensusNodeSpy.notCalled); - }); - }); - - withOverriddenEnvsInMochaTest({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: true }, () => { - it('should execute "eth_call" against consensus node with a ETH_CALL_DEFAULT_TO_CONSENSUS_NODE set to true', async function () { - restMock.onGet(`contracts/${defaultCallData.from}`).reply(404); - restMock.onGet(`accounts/${defaultCallData.from}${NO_TRANSACTIONS}`).reply( - 200, - JSON.stringify({ - account: '0.0.1723', - evm_address: defaultCallData.from, - }), - ); - restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, JSON.stringify(DEFAULT_CONTRACT)); - - await contractService.call( - { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, - 'latest', - requestDetails, - ); + it('should execute "eth_call"', async function () { + web3Mock.onPost('contracts/call').reply(200); + restMock.onGet(`contracts/${defaultCallData.from}`).reply(404); + restMock.onGet(`accounts/${defaultCallData.from}${NO_TRANSACTIONS}`).reply( + 200, + JSON.stringify({ + account: '0.0.1723', + evm_address: defaultCallData.from, + }), + ); + restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, JSON.stringify(DEFAULT_CONTRACT)); - assert(callMirrorNodeSpy.notCalled); - assert(callConsensusNodeSpy.calledOnce); - }); + await contractService.call( + { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, + 'latest', + requestDetails, + ); + assert(callMirrorNodeSpy.calledOnce); }); it('to field is not a contract or token', async function () { @@ -231,226 +177,12 @@ describe('@ethCall Eth Call spec', async function () { }); }); - describe('eth_call using consensus node', async function () { - overrideEnvsInMochaDescribe({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: true }); - - it('eth_call with no gas', async function () { - restMock.onGet(`contracts/${ACCOUNT_ADDRESS_1}`).reply(404); - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - - sdkClientStub.submitContractCallQueryWithRetry.resolves({ - asBytes: function () { - return Uint8Array.of(0); - }, - } as unknown as ContractFunctionResult); - const result = await contractService.call( - { - from: ACCOUNT_ADDRESS_1, - to: CONTRACT_ADDRESS_2, - data: CONTRACT_CALL_DATA, - }, - 'latest', - requestDetails, - ); - sinon.assert.calledWith( - sdkClientStub.submitContractCallQueryWithRetry, - CONTRACT_ADDRESS_2, - CONTRACT_CALL_DATA, - 400_000, - ACCOUNT_ADDRESS_1, - 'eth_call', - ); - expect(result).to.equal('0x00'); - }); - - it('eth_call with no data', async function () { - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - sdkClientStub.submitContractCallQueryWithRetry.resolves({ - asBytes: function () { - return Uint8Array.of(0); - }, - } as unknown as ContractFunctionResult); - - const result = await contractService.call( - { - from: ACCOUNT_ADDRESS_1, - to: CONTRACT_ADDRESS_2, - gas: MAX_GAS_LIMIT_HEX, - }, - 'latest', - requestDetails, - ); - - sinon.assert.calledWith( - sdkClientStub.submitContractCallQueryWithRetry, - CONTRACT_ADDRESS_2, - undefined, - MAX_GAS_LIMIT, - ACCOUNT_ADDRESS_1, - 'eth_call', - ); - expect(result).to.equal('0x00'); - }); - - it('eth_call with no "from" address', async function () { - const callData = { - ...defaultCallData, - from: undefined, - to: CONTRACT_ADDRESS_2, - data: CONTRACT_CALL_DATA, - gas: MAX_GAS_LIMIT, - }; - - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - sdkClientStub.submitContractCallQueryWithRetry.resolves({ - asBytes: function () { - return Uint8Array.of(0); - }, - } as unknown as ContractFunctionResult); - - const result = await contractService.call(callData, 'latest', requestDetails); - expect(result).to.equal('0x00'); - }); - - it('eth_call with a bad "from" address', async function () { - const callData = { - ...defaultCallData, - from: '0x00000000000000000000000000000000000000', - to: CONTRACT_ADDRESS_2, - data: CONTRACT_CALL_DATA, - gas: MAX_GAS_LIMIT, - }; - - const result = await contractService.call(callData, 'latest', requestDetails); - - expect((result as JsonRpcError).code).to.equal(-32014); - expect((result as JsonRpcError).message).to.equal( - `Non Existing Account Address: ${callData.from}. Expected an Account Address.`, - ); - }); - - it('eth_call with all fields', async function () { - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - sdkClientStub.submitContractCallQueryWithRetry.resolves({ - asBytes: function () { - return Uint8Array.of(0); - }, - } as unknown as ContractFunctionResult); - - const result = await contractService.call(ETH_CALL_REQ_ARGS, 'latest', requestDetails); - - sinon.assert.calledWith( - sdkClientStub.submitContractCallQueryWithRetry, - CONTRACT_ADDRESS_2, - CONTRACT_CALL_DATA, - MAX_GAS_LIMIT, - ACCOUNT_ADDRESS_1, - 'eth_call', - ); - expect(result).to.equal('0x00'); - }); - - //Return once the value, then it's being fetched from cache. After the loop we reset the sdkClientStub, so that it returns nothing, if we get an error in the next request that means that the cache was cleared. - it('eth_call should cache the response for 200ms', async function () { - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - sdkClientStub.submitContractCallQueryWithRetry.resolves({ - asBytes: function () { - return Uint8Array.of(0); - }, - } as unknown as ContractFunctionResult); - - for (let index = 0; index < 3; index++) { - const result = await contractService.call( - { - from: ACCOUNT_ADDRESS_1, - to: CONTRACT_ADDRESS_2, - data: CONTRACT_CALL_DATA, - gas: MAX_GAS_LIMIT_HEX, - }, - 'latest', - requestDetails, - ); - expect(result).to.equal('0x00'); - await new Promise((r) => setTimeout(r, 50)); - } - - await new Promise((r) => setTimeout(r, 200)); - - const expectedError = predefined.INVALID_CONTRACT_ADDRESS(CONTRACT_ADDRESS_2); - sdkClientStub.submitContractCallQueryWithRetry.throws(expectedError); - const call: string | JsonRpcError = await contractService.call(ETH_CALL_REQ_ARGS, 'latest', requestDetails); - - expect((call as JsonRpcError).code).to.equal(expectedError.code); - expect((call as JsonRpcError).message).to.equal(expectedError.message); - }); - - it('SDK returns a precheck error', async function () { - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - sdkClientStub.submitContractCallQueryWithRetry.throws( - predefined.CONTRACT_REVERT(defaultErrorMessageText, defaultErrorMessageHex), - ); - - const result = await contractService.call(ETH_CALL_REQ_ARGS, 'latest', requestDetails); - - expect(result).to.exist; - expect((result as JsonRpcError).code).to.equal(3); - expect((result as JsonRpcError).message).to.equal(`execution reverted: ${defaultErrorMessageText}`); - expect((result as JsonRpcError).data).to.equal(defaultErrorMessageHex); - }); - - it('eth_call with wrong `to` field', async function () { - const args = [ - { - from: CONTRACT_ADDRESS_1, - to: WRONG_CONTRACT_ADDRESS, - data: CONTRACT_CALL_DATA, - gas: MAX_GAS_LIMIT_HEX, - }, - 'latest', - requestDetails, - ]; - - await RelayAssertions.assertRejection( - predefined.INVALID_CONTRACT_ADDRESS(WRONG_CONTRACT_ADDRESS), - ethImpl.call, - false, - ethImpl, - args, - ); - }); - - it('eth_call throws internal error when consensus node times out and submitContractCallQueryWithRetry returns undefined', async function () { - restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, JSON.stringify(DEFAULT_CONTRACT_2)); - - sdkClientStub.submitContractCallQueryWithRetry.resolves(undefined); - - const result = await contractService.call( - { - to: CONTRACT_ADDRESS_2, - data: CONTRACT_CALL_DATA, - gas: 5_000_000, - }, - 'latest', - requestDetails, - ); - - expect(result).to.exist; - expect((result as JsonRpcError).code).to.equal(-32603); - expect((result as JsonRpcError).message).to.equal( - 'Error invoking RPC: Invalid contractCallResponse from consensus-node: undefined', - ); - }); - }); - describe('eth_call using mirror node', async function () { const defaultCallData = { gas: 400000, value: null, }; - overrideEnvsInMochaDescribe({ ETH_CALL_DEFAULT_TO_CONSENSUS_NODE: false }); - - //temporary workaround until precompiles are implemented in Mirror node evm module beforeEach(() => { restMock.onGet(`tokens/${defaultContractResults.results[1].contract_id}`).reply(404, null); web3Mock.reset(); diff --git a/packages/relay/tests/lib/formatters.spec.ts b/packages/relay/tests/lib/formatters.spec.ts index 447a3462fe..474b4f647e 100644 --- a/packages/relay/tests/lib/formatters.spec.ts +++ b/packages/relay/tests/lib/formatters.spec.ts @@ -10,7 +10,6 @@ import { formatRequestIdMessage, formatTransactionId, formatTransactionIdWithoutQueryParams, - getFunctionSelector, hexToASCII, isHex, isValidEthereumAddress, @@ -690,26 +689,6 @@ describe('Formatters', () => { }); }); - describe('getFunctionSelector', () => { - it('should return an empty string when input is an empty string or undefined', () => { - const result = getFunctionSelector(''); - expect(result).to.eq(''); - - const undefinedResult = getFunctionSelector(undefined); - expect(undefinedResult).to.eq(''); - }); - - it('should return the first 8 characters of a valid hex string without "0x"', () => { - const result = getFunctionSelector('1234567890abcdef'); - expect(result).to.eq('12345678'); - }); - - it('should return the first 8 characters of a valid hex string starting with "0x"', () => { - const result = getFunctionSelector('0x1234567890abcdef'); - expect(result).to.eq('12345678'); - }); - }); - describe('mapKeysAndValues', () => { it('should map keys and values correctly', () => { const target = { a: '1', b: '2', c: '3' }; diff --git a/packages/relay/tests/lib/utils.spec.ts b/packages/relay/tests/lib/utils.spec.ts index ba00fab49e..18fb104cf2 100644 --- a/packages/relay/tests/lib/utils.spec.ts +++ b/packages/relay/tests/lib/utils.spec.ts @@ -120,24 +120,18 @@ describe('Utils', () => { { keyFormat: 'DER', keyValue: PrivateKey.generateED25519().toStringDer() }, ]; - privateKeys.forEach(({ keyFormat, keyValue }) => { - withOverriddenEnvsInMochaTest( - { - OPERATOR_ID_ETH_SENDRAWTRANSACTION: accountId, - OPERATOR_KEY_ETH_SENDRAWTRANSACTION: keyValue, - OPERATOR_KEY_FORMAT: keyFormat, - }, - () => { - it(`should return operator credentials for "eth_sendRawTransaction" client type`, () => { - const operator = Utils.getOperator(logger, 'eth_sendRawTransaction'); - - expect(operator).to.not.be.null; - expect(operator?.accountId.toString()).to.equal(accountId); - expect(operator?.privateKey).to.deep.equal(Utils.createPrivateKeyBasedOnFormat(keyValue)); - }); - }, - ); - }); + withOverriddenEnvsInMochaTest( + { + OPERATOR_ID_MAIN: false, + OPERATOR_KEY_MAIN: false, + }, + () => { + it('should return null for invalid operator id or key', () => { + const operator = Utils.getOperator(logger); + expect(operator).to.be.null; + }); + }, + ); privateKeys.forEach(({ keyFormat, keyValue }) => { withOverriddenEnvsInMochaTest( diff --git a/packages/relay/tests/test.env b/packages/relay/tests/test.env index 7ce0ab8021..04cb8c2f71 100755 --- a/packages/relay/tests/test.env +++ b/packages/relay/tests/test.env @@ -14,7 +14,6 @@ HBAR_RATE_LIMIT_DURATION = 160000 SDK_REQUEST_TIMEOUT = 10000 MIRROR_NODE_RETRIES = 3 MIRROR_NODE_RETRY_DELAY = 250 -ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = true GAS_PRICE_TINY_BAR_BUFFER= 10000000000 CLIENT_TRANSPORT_SECURITY= false INPUT_SIZE_LIMIT= 1 diff --git a/packages/server/tests/acceptance/rpc_batch3.spec.ts b/packages/server/tests/acceptance/rpc_batch3.spec.ts index d8415165c8..eeb2df3670 100644 --- a/packages/server/tests/acceptance/rpc_batch3.spec.ts +++ b/packages/server/tests/acceptance/rpc_batch3.spec.ts @@ -527,43 +527,40 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () { expect(res).to.eq(basicContractJson.deployedBytecode); }); - // value is processed only when eth_call goes through the mirror node - if (!ConfigService.get('ETH_CALL_DEFAULT_TO_CONSENSUS_NODE')) { - it('010 Should call msgValue', async function () { - const callData = { - ...defaultCallData, - data: '0xddf363d7', - value: ONE_THOUSAND_TINYBARS, - }; - - const res = await relay.call(RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId); - expect(res).to.eq('0x00000000000000000000000000000000000000000000000000000000000003e8'); - }); - - // test is pending until fallback workflow to consensus node is removed, because this flow works when calling to consensus - xit('011 Should fail when calling msgValue with more value than available balance', async function () { - const callData = { - ...defaultCallData, - data: '0xddf363d7', - value: '0x3e80000000', - }; - const errorType = predefined.CONTRACT_REVERT(); - const args = [RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId]; - - await Assertions.assertPredefinedRpcError(errorType, relay.call, true, relay, args); - }); - - it("012 should work for wrong 'from' field", async function () { - const callData = { - from: '0x0000000000000000000000000000000000000000', - to: callerAddress, - data: '0x0ec1551d', - }; - - const res = await relay.call(RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId); - expect(res).to.eq('0x0000000000000000000000000000000000000000000000000000000000000004'); - }); - } + it('010 Should call msgValue', async function () { + const callData = { + ...defaultCallData, + data: '0xddf363d7', + value: ONE_THOUSAND_TINYBARS, + }; + + const res = await relay.call(RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId); + expect(res).to.eq('0x00000000000000000000000000000000000000000000000000000000000003e8'); + }); + + // test is pending until fallback workflow to consensus node is removed, because this flow works when calling to consensus + xit('011 Should fail when calling msgValue with more value than available balance', async function () { + const callData = { + ...defaultCallData, + data: '0xddf363d7', + value: '0x3e80000000', + }; + const errorType = predefined.CONTRACT_REVERT(); + const args = [RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId]; + + await Assertions.assertPredefinedRpcError(errorType, relay.call, true, relay, args); + }); + + it("012 should work for wrong 'from' field", async function () { + const callData = { + from: '0x0000000000000000000000000000000000000000', + to: callerAddress, + data: '0x0ec1551d', + }; + + const res = await relay.call(RelayCall.ETH_ENDPOINTS.ETH_CALL, [callData, 'latest'], requestId); + expect(res).to.eq('0x0000000000000000000000000000000000000000000000000000000000000004'); + }); }); } }); diff --git a/packages/server/tests/localAcceptance.env b/packages/server/tests/localAcceptance.env index e8b7c27401..e6e5e25f6d 100644 --- a/packages/server/tests/localAcceptance.env +++ b/packages/server/tests/localAcceptance.env @@ -11,7 +11,6 @@ MIRROR_NODE_URL=http://127.0.0.1:5551 SERVER_PORT=7546 RELAY_ENDPOINT=http://127.0.0.1:7546 MIRROR_NODE_LIMIT_PARAM=2 -ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=false HAPI_CLIENT_TRANSACTION_RESET=50 HAPI_CLIENT_DURATION_RESET=3600000 HAPI_CLIENT_ERROR_RESET=[21, 50] diff --git a/packages/server/tests/previewnetAcceptance.env b/packages/server/tests/previewnetAcceptance.env index f4d408ac5a..ecf2ba060c 100644 --- a/packages/server/tests/previewnetAcceptance.env +++ b/packages/server/tests/previewnetAcceptance.env @@ -7,7 +7,6 @@ WS_RELAY_URL=wss://previewnet.hashio.io/ws MIRROR_NODE_RETRIES=5 MIRROR_NODE_RETRY_DELAY=500 MIRROR_NODE_LIMIT_PARAM=2 -ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=false HAPI_CLIENT_TRANSACTION_RESET=50 HAPI_CLIENT_DURATION_RESET=3600000 HAPI_CLIENT_ERROR_RESET=[50] diff --git a/packages/server/tests/testnetAcceptance.env b/packages/server/tests/testnetAcceptance.env index 0f774e57f0..64cf6820fe 100644 --- a/packages/server/tests/testnetAcceptance.env +++ b/packages/server/tests/testnetAcceptance.env @@ -7,7 +7,6 @@ WS_RELAY_URL=wss://testnet.hashio.io/ws MIRROR_NODE_RETRIES=5 MIRROR_NODE_RETRY_DELAY=500 MIRROR_NODE_LIMIT_PARAM=2 -ETH_CALL_DEFAULT_TO_CONSENSUS_NODE=false HAPI_CLIENT_TRANSACTION_RESET=50 HAPI_CLIENT_DURATION_RESET=3600000 HAPI_CLIENT_ERROR_RESET=[50]