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

chore: cherry picked debug_traceTransaction #2630

Merged
merged 2 commits into from
Jun 27, 2024
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
5 changes: 5 additions & 0 deletions packages/relay/src/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ const formatContractResult = (cr: any) => {
return null;
};

const strip0x = (input: string): string => {
return input.startsWith(EMPTY_HEX) ? input.substring(2) : input;
};

const prepend0x = (input: string): string => {
return input.startsWith(EMPTY_HEX) ? input : EMPTY_HEX + input;
};
Expand Down Expand Up @@ -294,6 +298,7 @@ export {
trimPrecedingZeros,
weibarHexToTinyBarInt,
stringToHex,
strip0x,
toHexString,
isValidEthereumAddress,
isHex,
Expand Down
88 changes: 66 additions & 22 deletions packages/relay/src/lib/clients/mirrorNodeClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*-
/* -
*
* Hedera JSON RPC Relay
*
Expand All @@ -18,8 +18,8 @@
*
*/

import Axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { MirrorNodeClientError } from './../errors/MirrorNodeClientError';
import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { MirrorNodeClientError } from '../errors/MirrorNodeClientError';
import { Logger } from 'pino';
import constants from './../constants';
import { Histogram, Registry } from 'prom-client';
Expand All @@ -31,8 +31,9 @@ import { install as betterLookupInstall } from 'better-lookup';
import { CacheService } from '../services/cacheService/cacheService';
import { ethers } from 'ethers';

const http = require('http');
const https = require('https');
import http from 'http';
import https from 'https';
import { IOpcodesResponse } from './models/IOpcodesResponse';

type REQUEST_METHODS = 'GET' | 'POST';

Expand Down Expand Up @@ -73,6 +74,7 @@ export class MirrorNodeClient {
private static GET_CONTRACT_RESULTS_DETAILS_BY_ADDRESS_AND_TIMESTAMP_ENDPOINT = `contracts/${MirrorNodeClient.ADDRESS_PLACEHOLDER}/results/${MirrorNodeClient.TIMESTAMP_PLACEHOLDER}`;
private static GET_CONTRACT_RESULTS_DETAILS_BY_CONTRACT_ID_ENDPOINT = `contracts/${MirrorNodeClient.CONTRACT_ID_PLACEHOLDER}/results/${MirrorNodeClient.TIMESTAMP_PLACEHOLDER}`;
private static GET_CONTRACTS_RESULTS_ACTIONS = `contracts/results/${MirrorNodeClient.TRANSACTION_ID_PLACEHOLDER}/actions`;
private static GET_CONTRACTS_RESULTS_OPCODES = `contracts/results/${MirrorNodeClient.TRANSACTION_ID_PLACEHOLDER}/opcodes`;
private static GET_CONTRACT_RESULT_ENDPOINT = 'contracts/results/';
private static GET_CONTRACT_RESULT_LOGS_ENDPOINT = 'contracts/results/logs';
private static GET_CONTRACT_RESULT_LOGS_BY_ADDRESS_ENDPOINT = `contracts/${MirrorNodeClient.ADDRESS_PLACEHOLDER}/results/logs`;
Expand Down Expand Up @@ -103,6 +105,7 @@ export class MirrorNodeClient {
[MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT, [404]],
[MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_BY_ADDRESS_ENDPOINT, [404]],
[MirrorNodeClient.GET_CONTRACT_RESULTS_ENDPOINT, [404]],
[MirrorNodeClient.GET_CONTRACTS_RESULTS_OPCODES, [404]],
[MirrorNodeClient.GET_NETWORK_EXCHANGERATE_ENDPOINT, [404]],
[MirrorNodeClient.GET_NETWORK_FEES_ENDPOINT, [404]],
[MirrorNodeClient.GET_TOKENS_ENDPOINT, [404]],
Expand Down Expand Up @@ -149,9 +152,18 @@ export class MirrorNodeClient {
*/
private readonly register: Registry;

private mirrorResponseHistogram;
/**
* The histogram used for tracking the response time of the mirror node.
* @private
*/
private readonly mirrorResponseHistogram: Histogram;

/**
* The cache service used for caching responses.
* @private
*/
private readonly cacheService: CacheService;

static readonly EVM_ADDRESS_REGEX: RegExp = /\/accounts\/([\d\.]+)/;

static mirrorNodeContractResultsPageMax = parseInt(process.env.MIRROR_NODE_CONTRACT_RESULTS_PG_MAX!) || 25;
Expand Down Expand Up @@ -304,26 +316,23 @@ export class MirrorNodeClient {
return `${baseUrl}${MirrorNodeClient.API_V1_POST_FIX}`;
}

private async request(
private async request<T>(
path: string,
pathLabel: string,
method: REQUEST_METHODS,
data?: any,
requestIdPrefix?: string,
retries?: number,
): Promise<any> {
): Promise<T | null> {
const start = Date.now();
// extract request id from prefix and remove trailing ']' character
const requestId =
requestIdPrefix
?.split(MirrorNodeClient.REQUEST_PREFIX_SEPARATOR)[1]
.replace(MirrorNodeClient.REQUEST_PREFIX_TRAILING_BRACKET, MirrorNodeClient.EMPTY_STRING) ||
MirrorNodeClient.EMPTY_STRING;
let ms;
const controller = new AbortController();
try {
let response;

const axiosRequestConfig: AxiosRequestConfig = {
headers: {
[MirrorNodeClient.REQUESTID_LABEL]: requestId,
Expand All @@ -336,18 +345,23 @@ export class MirrorNodeClient {
axiosRequestConfig['axios-retry'] = { retries };
}

let response: AxiosResponse<T, any>;
if (method === MirrorNodeClient.HTTP_GET) {
response = await this.restClient.get(path, axiosRequestConfig);
if (pathLabel == MirrorNodeClient.GET_CONTRACTS_RESULTS_OPCODES) {
response = await this.web3Client.get<T>(path, axiosRequestConfig);
} else {
response = await this.restClient.get<T>(path, axiosRequestConfig);
}
} else {
response = await this.web3Client.post(path, data, axiosRequestConfig);
response = await this.web3Client.post<T>(path, data, axiosRequestConfig);
}

const ms = Date.now() - start;
this.logger.debug(`${requestId} [${method}] ${path} ${response.status} ${ms} ms`);
this.mirrorResponseHistogram.labels(pathLabel, response.status).observe(ms);
this.mirrorResponseHistogram.labels(pathLabel, response.status?.toString()).observe(ms);
return response.data;
} catch (error: any) {
ms = Date.now() - start;
const ms = Date.now() - start;
const effectiveStatusCode =
error.response?.status ||
MirrorNodeClientError.ErrorCodes[error.code] ||
Expand All @@ -363,15 +377,25 @@ export class MirrorNodeClient {
return null;
}

async get(path: string, pathLabel: string, requestIdPrefix?: string, retries?: number): Promise<any> {
return this.request(path, pathLabel, 'GET', null, requestIdPrefix, retries);
async get<T = any>(path: string, pathLabel: string, requestIdPrefix?: string, retries?: number): Promise<T | null> {
return this.request<T>(path, pathLabel, 'GET', null, requestIdPrefix, retries);
}

async post(path: string, data: any, pathLabel: string, requestIdPrefix?: string, retries?: number): Promise<any> {
async post<T = any>(
path: string,
data: any,
pathLabel: string,
requestIdPrefix?: string,
retries?: number,
): Promise<T | null> {
if (!data) data = {};
return this.request(path, pathLabel, 'POST', data, requestIdPrefix, retries);
return this.request<T>(path, pathLabel, 'POST', data, requestIdPrefix, retries);
}

/**
* @returns null if the error code is in the accepted error responses,
* @throws MirrorNodeClientError if the error code is not in the accepted error responses.
*/
handleError(
error: any,
path: string,
Expand All @@ -383,7 +407,7 @@ export class MirrorNodeClient {
const mirrorError = new MirrorNodeClientError(error, effectiveStatusCode);
const acceptedErrorResponses = MirrorNodeClient.acceptedErrorStatusesResponsePerRequestPathMap.get(pathLabel);

if (error.response && acceptedErrorResponses && acceptedErrorResponses.indexOf(effectiveStatusCode) !== -1) {
if (error.response && acceptedErrorResponses?.includes(effectiveStatusCode)) {
this.logger.debug(`${requestIdPrefix} [${method}] ${path} ${effectiveStatusCode} status`);
return null;
}
Expand Down Expand Up @@ -699,7 +723,7 @@ export class MirrorNodeClient {
* the mirror node DB and `transaction_index` or `block_number` is returned as `undefined`. A single re-fetch is sufficient to
* resolve this problem.
* @param transactionIdOrHash
* @param requestId
* @param requestIdPrefix
*/
public async getContractResultWithRetry(transactionIdOrHash: string, requestIdPrefix?: string) {
const contractResult = await this.getContractResult(transactionIdOrHash, requestIdPrefix);
Expand Down Expand Up @@ -745,6 +769,19 @@ export class MirrorNodeClient {
);
}

public async getContractsResultsOpcodes(
transactionIdOrHash: string,
requestIdPrefix?: string,
params?: { memory?: boolean; stack?: boolean; storage?: boolean },
): Promise<IOpcodesResponse | null> {
const queryParams = params ? this.getQueryParams(params) : '';
return this.get<IOpcodesResponse>(
`${this.getContractResultsOpcodesByTransactionIdPath(transactionIdOrHash)}${queryParams}`,
MirrorNodeClient.GET_CONTRACTS_RESULTS_OPCODES,
requestIdPrefix,
);
}

public async getContractResultsByAddress(
contractIdOrAddress: string,
contractResultsParams?: IContractResultsParams,
Expand Down Expand Up @@ -928,6 +965,13 @@ export class MirrorNodeClient {
);
}

private getContractResultsOpcodesByTransactionIdPath(transactionIdOrHash: string) {
return MirrorNodeClient.GET_CONTRACTS_RESULTS_OPCODES.replace(
MirrorNodeClient.TRANSACTION_ID_PLACEHOLDER,
transactionIdOrHash,
);
}

public async getTokenById(tokenId: string, requestIdPrefix?: string, retries?: number) {
return this.get(
`${MirrorNodeClient.GET_TOKENS_ENDPOINT}/${tokenId}`,
Expand Down Expand Up @@ -1009,7 +1053,6 @@ export class MirrorNodeClient {
* Check if transaction fail is because of contract revert and try to fetch and log the reason.
*
* @param e
* @param requestId
* @param requestIdPrefix
*/
public async getContractRevertReasonFromTransaction(e: any, requestIdPrefix: string): Promise<any | undefined> {
Expand Down Expand Up @@ -1087,6 +1130,7 @@ export class MirrorNodeClient {
* @param searchableTypes the types to search for
* @param callerName calling method name
* @param requestIdPrefix the request id prefix message
* @param retries the number of retries
* @returns entity object or null if not found
*/
public async resolveEntityType(
Expand Down
31 changes: 31 additions & 0 deletions packages/relay/src/lib/clients/models/IOpcode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* -
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 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 interface IOpcode {
pc?: number;
op?: string;
gas?: number;
gas_cost?: number;
depth?: number;
stack?: string[];
memory?: string[];
storage?: { [key: string]: string };
reason?: string | null;
}
30 changes: 30 additions & 0 deletions packages/relay/src/lib/clients/models/IOpcodesResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* -
*
* Hedera JSON RPC Relay
*
* Copyright (C) 2024 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 { IOpcode } from './IOpcode';

export interface IOpcodesResponse {
address?: string;
contract_id?: string;
gas?: number;
failed?: boolean;
return_value?: string;
opcodes?: IOpcode[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import type { TracerType } from '../../constants';

export interface IDebugService {
debug_traceTransaction: (
transactionHash: string,
transactionIdOrHash: string,
tracer: TracerType,
tracerConfig: object,
requestIdPrefix?: string,
Expand Down
Loading
Loading