diff --git a/docs/design/distributed-cache.md b/docs/design/distributed-cache.md index 0a323695d7..bf9178f3bb 100644 --- a/docs/design/distributed-cache.md +++ b/docs/design/distributed-cache.md @@ -19,9 +19,9 @@ Important details is that, if an operator does not want to use Redis or it went ```javascript interface ICacheClient { - get(key: string, callingMethod: string, requestIdPrefix?: string): any; - set(key: string, value: any, callingMethod: string, ttl?: number, requestIdPrefix?: string): void; - delete(key: string, callingMethod: string, requestIdPrefix?: string): void; + get(key: string, callingMethod: string): any; + set(key: string, value: any, callingMethod: string, ttl?: number): void; + delete(key: string, callingMethod: string): void; clear(): void; } ``` @@ -40,15 +40,15 @@ class LocalLRUCache implements ICacheClient{ public constructor() { this.cache = new LRU(this.options); } - get(key: string, callingMethod: string, requestIdPrefix?: string) { + get(key: string, callingMethod: string) { // Get item from internal cache implementation } - set(key: string, value: any, callingMethod: string, ttl?: number, requestIdPrefix?: string) { + set(key: string, value: any, callingMethod: string, ttl?: number) { // Set item to internal cache implementation } - delete(key: string, callingMethod: string, requestIdPrefix?: string) { + delete(key: string, callingMethod: string) { // Delete item from internal cache implementation } @@ -81,15 +81,15 @@ class RedisCache implements ICacheClient{ this.cache = client; } - get(key: string, callingMethod: string, requestIdPrefix?: string) { + get(key: string, callingMethod: string) { // Get item from shared cache implementation } - set(key: string, value: any, callingMethod: string, ttl?: number, requestIdPrefix?: string) { + set(key: string, value: any, callingMethod: string, ttl?: number) { // Set item to shared cache implementation } - delete(key: string, callingMethod: string, requestIdPrefix?: string) { + delete(key: string, callingMethod: string) { // Delete item from shared cache implementation } @@ -120,19 +120,19 @@ class CacheClientService{ const sharedCache = new RedisCache(); } - get(key: string, callingMethod: string, requestIdPrefix?: string, shared: boolean = false) { + get(key: string, callingMethod: string, shared: boolean = false) { // Depending on the shared boolean, this method decide from where it should request the data. // Fallbacks to internalCache in case of error from the shared cache. // Getting from shared cache depends on REDIS_ENABLED env. variable } - set(key: string, value: any, callingMethod: string, ttl?: number, requestIdPrefix?: string, shared: boolean = false) { + set(key: string, value: any, callingMethod: string, ttl?: number, shared: boolean = false) { // Depending on the shared boolean, this method decide where it should save the data. // Fallbacks to internalCache in case of error from the shared cache. // Setting to shared cache depends on REDIS_ENABLED env. variable } - delete(key: string, callingMethod: string, requestIdPrefix?: string, shared: boolean = false) { + delete(key: string, callingMethod: string, shared: boolean = false) { // Depending on the shared boolean, this method decide from where it should delete the data. // Fallbacks to internalCache in case of error from the shared cache. // Deleting from shared cache depends on REDIS_ENABLED env. variable diff --git a/package-lock.json b/package-lock.json index 98bace7110..00ae987ec8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "@types/chai-as-promised": "^7.1.5", "@types/co-body": "6.1.0", "@types/koa-cors": "^0.0.6", + "@types/lodash": "^4.17.7", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", "axios-mock-adapter": "^1.20.0", @@ -5154,6 +5155,13 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", @@ -27177,6 +27185,12 @@ "@types/node": "*" } }, + "@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true + }, "@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", diff --git a/package.json b/package.json index e0e50040d4..e3876d60df 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "@types/chai-as-promised": "^7.1.5", "@types/co-body": "6.1.0", "@types/koa-cors": "^0.0.6", + "@types/lodash": "^4.17.7", "@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/parser": "^6.5.0", "axios-mock-adapter": "^1.20.0", diff --git a/packages/relay/src/formatters.ts b/packages/relay/src/formatters.ts index 872787b4e5..3087c9fe5d 100644 --- a/packages/relay/src/formatters.ts +++ b/packages/relay/src/formatters.ts @@ -21,9 +21,7 @@ import crypto from 'crypto'; import constants from './lib/constants'; import { BigNumber as BN } from 'bignumber.js'; -import { TransactionRecord } from '@hashgraph/sdk'; import { BigNumber } from '@hashgraph/sdk/lib/Transfer'; -import { MirrorNodeTransactionRecord } from './lib/types/mirrorNode'; import { Transaction, Transaction1559, Transaction2930 } from './lib/model'; const EMPTY_HEX = '0x'; diff --git a/packages/relay/src/index.ts b/packages/relay/src/index.ts index 3c95d53ce5..1e16a7db9f 100644 --- a/packages/relay/src/index.ts +++ b/packages/relay/src/index.ts @@ -19,7 +19,7 @@ */ import { Block, Log, Receipt, Transaction } from './lib/model'; -import { IContractCallRequest } from './lib/types'; +import { IContractCallRequest, RequestDetails } from './lib/types'; import { JsonRpcError, predefined } from './lib/errors/JsonRpcError'; import WebSocketError from './lib/errors/WebSocketError'; import { MirrorNodeClientError } from './lib/errors/MirrorNodeClientError'; @@ -62,33 +62,33 @@ export interface Net { } export interface Eth { - blockNumber(requestId?: string): Promise; + blockNumber(requestDetails: RequestDetails): Promise; - call(call: any, blockParam: string | object | null, requestId?: string): Promise; + call(call: any, blockParam: string | object | null, requestDetails: RequestDetails): Promise; - coinbase(requestId?: string): JsonRpcError; + coinbase(requestDetails: RequestDetails): JsonRpcError; estimateGas( transaction: IContractCallRequest, blockParam: string | null, - requestId?: string, + requestDetails: RequestDetails, ): Promise; - gasPrice(requestId?: string): Promise; + gasPrice(requestDetails: RequestDetails): Promise; - getBalance(account: string, blockNumber: string | null, requestId?: string): Promise; + getBalance(account: string, blockNumber: string | null, requestDetails: RequestDetails): Promise; - getBlockByHash(hash: string, showDetails: boolean, requestId?: string): Promise; + getBlockByHash(hash: string, showDetails: boolean, requestDetails: RequestDetails): Promise; - getBlockByNumber(blockNum: string, showDetails: boolean, requestId?: string): Promise; + getBlockByNumber(blockNum: string, showDetails: boolean, requestDetails: RequestDetails): Promise; - getBlockTransactionCountByHash(hash: string, requestId?: string): Promise; + getBlockTransactionCountByHash(hash: string, requestDetails: RequestDetails): Promise; - getBlockTransactionCountByNumber(blockNum: string, requestId?: string): Promise; + getBlockTransactionCountByNumber(blockNum: string, requestDetails: RequestDetails): Promise; - getCode(address: string, blockNumber: string | null, requestId?: string): Promise; + getCode(address: string, blockNumber: string | null, requestDetails: RequestDetails): Promise; - chainId(requestId?: string): string; + chainId(requestDetails: RequestDetails): string; getLogs( blockHash: string | null, @@ -96,61 +96,78 @@ export interface Eth { toBlock: string | null, address: string | string[] | null, topics: any[] | null, - requestId?: string, + requestDetails: RequestDetails, ): Promise; - getStorageAt(address: string, slot: string, blockNumber: string | null, requestId?: string): Promise; - - getTransactionByBlockHashAndIndex(hash: string, index: string, requestId?: string): Promise; - - getTransactionByBlockNumberAndIndex(blockNum: string, index: string, requestId?: string): Promise; - - getTransactionByHash(hash: string, requestId?: string): Promise; - - getTransactionCount(address: string, blockNum: string, requestId?: string): Promise; + getStorageAt( + address: string, + slot: string, + requestDetails: RequestDetails, + blockNumber: string | null, + ): Promise; + + getTransactionByBlockHashAndIndex( + hash: string, + index: string, + requestDetails: RequestDetails, + ): Promise; + + getTransactionByBlockNumberAndIndex( + blockNum: string, + index: string, + requestDetails: RequestDetails, + ): Promise; + + getTransactionByHash(hash: string, requestDetails: RequestDetails): Promise; + + getTransactionCount( + address: string, + blockNum: string, + requestDetails: RequestDetails, + ): Promise; - getTransactionReceipt(hash: string, requestId?: string): Promise; + getTransactionReceipt(hash: string, requestDetails: RequestDetails): Promise; - getUncleByBlockHashAndIndex(requestId?: string): Promise; + getUncleByBlockHashAndIndex(requestDetails: RequestDetails): Promise; - getUncleByBlockNumberAndIndex(requestId?: string): Promise; + getUncleByBlockNumberAndIndex(requestDetails: RequestDetails): Promise; - getUncleCountByBlockHash(requestId?: string): Promise; + getUncleCountByBlockHash(requestDetails: RequestDetails): Promise; - getUncleCountByBlockNumber(requestId?: string): Promise; + getUncleCountByBlockNumber(requestDetails: RequestDetails): Promise; - getWork(requestId?: string): JsonRpcError; + getWork(requestDetails: RequestDetails): JsonRpcError; feeHistory( blockCount: number, newestBlock: string, rewardPercentiles: Array | null, - requestId?: string, + requestDetails: RequestDetails, ): Promise; - hashrate(requestId?: string): Promise; + hashrate(requestDetails: RequestDetails): Promise; - maxPriorityFeePerGas(requestId?: string): Promise; + maxPriorityFeePerGas(requestDetails: RequestDetails): Promise; - mining(requestId?: string): Promise; + mining(requestDetails: RequestDetails): Promise; - protocolVersion(requestId?: string): JsonRpcError; + protocolVersion(requestDetails: RequestDetails): JsonRpcError; - sendRawTransaction(transaction: string, requestId: string): Promise; + sendRawTransaction(transaction: string, requestDetails: RequestDetails): Promise; - sendTransaction(requestId?: string): JsonRpcError; + sendTransaction(requestDetails: RequestDetails): JsonRpcError; - sign(requestId?: string): JsonRpcError; + sign(requestDetails: RequestDetails): JsonRpcError; - signTransaction(requestId?: string): JsonRpcError; + signTransaction(requestDetails: RequestDetails): JsonRpcError; - submitHashrate(requestId?: string): JsonRpcError; + submitHashrate(requestDetails: RequestDetails): JsonRpcError; - submitWork(requestId?: string): Promise; + submitWork(requestDetails: RequestDetails): Promise; - syncing(requestId?: string): Promise; + syncing(requestDetails: RequestDetails): Promise; - accounts(requestId?: string): Array; + accounts(requestDetails: RequestDetails): Array; filterService(): IFilterService; diff --git a/packages/relay/src/lib/clients/cache/ICacheClient.ts b/packages/relay/src/lib/clients/cache/ICacheClient.ts index b0a2241a17..4ca11b1fa0 100644 --- a/packages/relay/src/lib/clients/cache/ICacheClient.ts +++ b/packages/relay/src/lib/clients/cache/ICacheClient.ts @@ -18,17 +18,19 @@ * */ +import { RequestDetails } from '../../types'; + export interface ICacheClient { - keys(pattern: string, callingMethod: string, requestIdPrefix?: string): Promise; - get(key: string, callingMethod: string, requestIdPrefix?: string): Promise; - set(key: string, value: any, callingMethod: string, ttl?: number, requestIdPrefix?: string): Promise; - multiSet(keyValuePairs: Record, callingMethod: string, requestIdPrefix?: string): Promise; + keys(pattern: string, callingMethod: string, requestDetails: RequestDetails): Promise; + get(key: string, callingMethod: string, requestDetails: RequestDetails): Promise; + set(key: string, value: any, callingMethod: string, requestDetails: RequestDetails, ttl?: number): Promise; + multiSet(keyValuePairs: Record, callingMethod: string, requestDetails: RequestDetails): Promise; pipelineSet( keyValuePairs: Record, callingMethod: string, + requestDetails: RequestDetails, ttl?: number | undefined, - requestIdPrefix?: string, ): Promise; - delete(key: string, callingMethod: string, requestIdPrefix?: string): Promise; + delete(key: string, callingMethod: string, requestDetails: RequestDetails): Promise; clear(): Promise; } diff --git a/packages/relay/src/lib/clients/cache/IRedisCacheClient.ts b/packages/relay/src/lib/clients/cache/IRedisCacheClient.ts index 704c174335..944b02c60b 100644 --- a/packages/relay/src/lib/clients/cache/IRedisCacheClient.ts +++ b/packages/relay/src/lib/clients/cache/IRedisCacheClient.ts @@ -19,10 +19,17 @@ */ import type { ICacheClient } from './ICacheClient'; +import { RequestDetails } from '../../types'; export interface IRedisCacheClient extends ICacheClient { disconnect: () => Promise; - incrBy(key: string, amount: number, callingMethod: string, requestIdPrefix?: string): Promise; - rPush(key: string, value: any, callingMethod: string, requestIdPrefix?: string): Promise; - lRange(key: string, start: number, end: number, callingMethod: string, requestIdPrefix?: string): Promise; + incrBy(key: string, amount: number, callingMethod: string, requestDetails: RequestDetails): Promise; + rPush(key: string, value: any, callingMethod: string, requestDetails: RequestDetails): Promise; + lRange( + key: string, + start: number, + end: number, + callingMethod: string, + requestDetails: RequestDetails, + ): Promise; } diff --git a/packages/relay/src/lib/clients/cache/localLRUCache.ts b/packages/relay/src/lib/clients/cache/localLRUCache.ts index 0280e0319d..c366b8897d 100644 --- a/packages/relay/src/lib/clients/cache/localLRUCache.ts +++ b/packages/relay/src/lib/clients/cache/localLRUCache.ts @@ -23,6 +23,7 @@ import { Gauge, Registry } from 'prom-client'; import { ICacheClient } from './ICacheClient'; import constants from '../../constants'; import LRUCache, { LimitedByCount, LimitedByTTL } from 'lru-cache'; +import { RequestDetails } from '../../types'; /** * Represents a LocalLRUCache instance that uses an LRU (Least Recently Used) caching strategy @@ -98,14 +99,16 @@ export class LocalLRUCache implements ICacheClient { * If the value exists in the cache, updates metrics and logs the retrieval. * @param {string} key - The key associated with the cached value. * @param {string} callingMethod - The name of the method calling the cache. - * @param {string} requestIdPrefix - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {*} The cached value if found, otherwise null. */ - public async get(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + public async get(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { const value = this.cache.get(key); if (value !== undefined) { this.logger.trace( - `${requestIdPrefix} returning cached value ${key}:${JSON.stringify(value)} on ${callingMethod} call`, + `${requestDetails.formattedRequestId} returning cached value ${key}:${JSON.stringify( + value, + )} on ${callingMethod} call`, ); return value; } @@ -117,12 +120,14 @@ export class LocalLRUCache implements ICacheClient { * The remaining TTL of the specified key in the cache. * @param {string} key - The key to check the remaining TTL for. * @param {string} callingMethod - The name of the method calling the cache. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The remaining TTL in milliseconds. */ - public async getRemainingTtl(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + public async getRemainingTtl(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { const remainingTtl = this.cache.getRemainingTTL(key); // in milliseconds - this.logger.trace(`${requestIdPrefix} returning remaining TTL ${key}:${remainingTtl} on ${callingMethod} call`); + this.logger.trace( + `${requestDetails.formattedRequestId} returning remaining TTL ${key}:${remainingTtl} on ${callingMethod} call`, + ); return remainingTtl; } @@ -133,17 +138,19 @@ export class LocalLRUCache implements ICacheClient { * @param {*} value - The value to cache. * @param {string} callingMethod - The name of the method calling the cache. * @param {number} ttl - Time to live for the cached value in milliseconds (optional). - * @param {string} requestIdPrefix - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ public async set( key: string, value: any, callingMethod: string, + requestDetails: RequestDetails, ttl?: number, - requestIdPrefix?: string, ): Promise { const resolvedTtl = ttl ?? this.options.ttl; - this.logger.trace(`${requestIdPrefix} caching ${key}:${JSON.stringify(value)} for ${resolvedTtl} ms`); + this.logger.trace( + `${requestDetails.formattedRequestId} caching ${key}:${JSON.stringify(value)} for ${resolvedTtl} ms`, + ); this.cache.set(key, value, { ttl: resolvedTtl }); } @@ -152,17 +159,17 @@ export class LocalLRUCache implements ICacheClient { * * @param keyValuePairs - An object where each property is a key and its value is the value to be cached. * @param callingMethod - The name of the calling method. - * @param requestIdPrefix - Optional request ID prefix for logging. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves when the values are cached. */ public async multiSet( keyValuePairs: Record, callingMethod: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { // Iterate over each entry in the keyValuePairs object for (const [key, value] of Object.entries(keyValuePairs)) { - await this.set(key, value, callingMethod, undefined, requestIdPrefix); + await this.set(key, value, callingMethod, requestDetails); } } @@ -172,18 +179,18 @@ export class LocalLRUCache implements ICacheClient { * @param keyValuePairs - An object where each property is a key and its value is the value to be cached. * @param callingMethod - The name of the calling method. * @param ttl - Time to live on the set values - * @param requestIdPrefix - Optional request ID prefix for logging. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {void} A Promise that resolves when the values are cached. */ public async pipelineSet( keyValuePairs: Record, callingMethod: string, + requestDetails: RequestDetails, ttl?: number, - requestIdPrefix?: string, ): Promise { // Iterate over each entry in the keyValuePairs object for (const [key, value] of Object.entries(keyValuePairs)) { - await this.set(key, value, callingMethod, ttl, requestIdPrefix); + await this.set(key, value, callingMethod, requestDetails, ttl); } } @@ -192,10 +199,10 @@ export class LocalLRUCache implements ICacheClient { * Logs the deletion of the cache entry. * @param {string} key - The key associated with the cached value to delete. * @param {string} callingMethod - The name of the method calling the cache. - * @param {string} requestIdPrefix - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - public async delete(key: string, callingMethod: string, requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} delete cache for ${key}`); + public async delete(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} delete cache for ${key}`); this.cache.delete(key); } @@ -219,10 +226,10 @@ export class LocalLRUCache implements ICacheClient { * Retrieves all keys in the cache that match the given pattern. * @param {string} pattern - The pattern to match keys against. * @param {string} callingMethod - The name of the method calling the cache. - * @param {string} requestIdPrefix - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} An array of keys that match the pattern. */ - public async keys(pattern: string, callingMethod: string, requestIdPrefix?: string): Promise { + public async keys(pattern: string, callingMethod: string, requestDetails: RequestDetails): Promise { const keys = Array.from(this.cache.rkeys()); // Replace escaped special characters with placeholders @@ -251,7 +258,9 @@ export class LocalLRUCache implements ICacheClient { const matchingKeys = keys.filter((key) => regex.test(key)); - this.logger.trace(`${requestIdPrefix} retrieving keys matching ${pattern} on ${callingMethod} call`); + this.logger.trace( + `${requestDetails.formattedRequestId} retrieving keys matching ${pattern} on ${callingMethod} call`, + ); return matchingKeys; } } diff --git a/packages/relay/src/lib/clients/cache/redisCache.ts b/packages/relay/src/lib/clients/cache/redisCache.ts index a2ed6867b5..2c9263d239 100644 --- a/packages/relay/src/lib/clients/cache/redisCache.ts +++ b/packages/relay/src/lib/clients/cache/redisCache.ts @@ -25,6 +25,7 @@ import { Registry } from 'prom-client'; import { RedisCacheError } from '../../errors/RedisCacheError'; import constants from '../../constants'; import { IRedisCacheClient } from './IRedisCacheClient'; +import { RequestDetails } from '../../types'; /** * A class that provides caching functionality using Redis. @@ -125,15 +126,17 @@ export class RedisCache implements IRedisCacheClient { * * @param {string} key - The cache key. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The cached value or null if not found. */ - async get(key: string, callingMethod: string, requestIdPrefix?: string | undefined): Promise { + async get(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { const client = await this.getConnectedClient(); const result = await client.get(key); if (result) { this.logger.trace( - `${requestIdPrefix} returning cached value ${key}:${JSON.stringify(result)} on ${callingMethod} call`, + `${requestDetails.formattedRequestId} returning cached value ${key}:${JSON.stringify( + result, + )} on ${callingMethod} call`, ); // TODO: add metrics return JSON.parse(result); @@ -148,22 +151,24 @@ export class RedisCache implements IRedisCacheClient { * @param {*} value - The value to be cached. * @param {string} callingMethod - The name of the calling method. * @param {number} [ttl] - The time-to-live (expiration) of the cache item in milliseconds. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves when the value is cached. */ async set( key: string, value: any, callingMethod: string, - ttl?: number | undefined, - requestIdPrefix?: string | undefined, + requestDetails: RequestDetails, + ttl?: number, ): Promise { const client = await this.getConnectedClient(); const serializedValue = JSON.stringify(value); const resolvedTtl = ttl ?? this.options.ttl; // in milliseconds await client.set(key, serializedValue, { PX: resolvedTtl }); - this.logger.trace(`${requestIdPrefix} caching ${key}: ${serializedValue} on ${callingMethod} for ${resolvedTtl} s`); + this.logger.trace( + `${requestDetails.formattedRequestId} caching ${key}: ${serializedValue} on ${callingMethod} for ${resolvedTtl} s`, + ); // TODO: add metrics } @@ -172,10 +177,14 @@ export class RedisCache implements IRedisCacheClient { * * @param {Record} keyValuePairs - An object where each property is a key and its value is the value to be cached. * @param {string} callingMethod - The name of the calling method. - * @param {string} requestIdPrefix - Optional request ID prefix for logging. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves when the values are cached. */ - async multiSet(keyValuePairs: Record, callingMethod: string, requestIdPrefix?: string): Promise { + async multiSet( + keyValuePairs: Record, + callingMethod: string, + requestDetails: RequestDetails, + ): Promise { const client = await this.getConnectedClient(); // Serialize values const serializedKeyValuePairs: Record = {}; @@ -192,7 +201,9 @@ export class RedisCache implements IRedisCacheClient { // Log the operation const entriesLength = Object.keys(keyValuePairs).length; - this.logger.trace(`${requestIdPrefix} caching multiple keys via ${callingMethod}, total keys: ${entriesLength}`); + this.logger.trace( + `${requestDetails.formattedRequestId} caching multiple keys via ${callingMethod}, total keys: ${entriesLength}`, + ); } /** @@ -201,14 +212,14 @@ export class RedisCache implements IRedisCacheClient { * @param {Record} keyValuePairs - An object where each property is a key and its value is the value to be cached. * @param {string} callingMethod - The name of the calling method. * @param {number} [ttl] - The time-to-live (expiration) of the cache item in milliseconds. - * @param {string} requestIdPrefix - Optional request ID prefix for logging. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves when the values are cached. */ async pipelineSet( keyValuePairs: Record, callingMethod: string, - ttl?: number | undefined, - requestIdPrefix?: string, + requestDetails: RequestDetails, + ttl?: number, ): Promise { const client = await this.getConnectedClient(); const resolvedTtl = ttl ?? this.options.ttl; // in milliseconds @@ -225,7 +236,9 @@ export class RedisCache implements IRedisCacheClient { // Log the operation const entriesLength = Object.keys(keyValuePairs).length; - this.logger.trace(`${requestIdPrefix} caching multiple keys via ${callingMethod}, total keys: ${entriesLength}`); + this.logger.trace( + `${requestDetails.formattedRequestId} caching multiple keys via ${callingMethod}, total keys: ${entriesLength}`, + ); } /** @@ -233,13 +246,13 @@ export class RedisCache implements IRedisCacheClient { * * @param {string} key - The cache key. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves when the value is deleted from the cache. */ - async delete(key: string, callingMethod: string, requestIdPrefix?: string | undefined): Promise { + async delete(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { const client = await this.getConnectedClient(); await client.del(key); - this.logger.trace(`${requestIdPrefix} delete cache for ${key} on ${callingMethod} call`); + this.logger.trace(`${requestDetails.formattedRequestId} delete cache for ${key} on ${callingMethod} call`); // TODO: add metrics } @@ -301,13 +314,13 @@ export class RedisCache implements IRedisCacheClient { * @param {string} key The key to increment * @param {number} amount The amount to increment by * @param {string} callingMethod The name of the calling method - * @param {string} [requestIdPrefix] The optional request ID prefix + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The value of the key after incrementing */ - async incrBy(key: string, amount: number, callingMethod: string, requestIdPrefix?: string): Promise { + async incrBy(key: string, amount: number, callingMethod: string, requestDetails: RequestDetails): Promise { const client = await this.getConnectedClient(); const result = await client.incrBy(key, amount); - this.logger.trace(`${requestIdPrefix} incrementing ${key} by ${amount} on ${callingMethod} call`); + this.logger.trace(`${requestDetails.formattedRequestId} incrementing ${key} by ${amount} on ${callingMethod} call`); return result; } @@ -318,7 +331,7 @@ export class RedisCache implements IRedisCacheClient { * @param {number} start The start index * @param {number} end The end index * @param {string} callingMethod The name of the calling method - * @param {string} [requestIdPrefix] The optional request ID prefix + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The list of elements in the range */ async lRange( @@ -326,11 +339,13 @@ export class RedisCache implements IRedisCacheClient { start: number, end: number, callingMethod: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { const client = await this.getConnectedClient(); const result = await client.lRange(key, start, end); - this.logger.trace(`${requestIdPrefix} retrieving range [${start}:${end}] from ${key} on ${callingMethod} call`); + this.logger.trace( + `${requestDetails.formattedRequestId} retrieving range [${start}:${end}] from ${key} on ${callingMethod} call`, + ); return result.map((item) => JSON.parse(item)); } @@ -340,14 +355,16 @@ export class RedisCache implements IRedisCacheClient { * @param {string} key The key of the list * @param {*} value The value to push * @param {string} callingMethod The name of the calling method - * @param {string} [requestIdPrefix] The optional request ID prefix + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The length of the list after pushing */ - async rPush(key: string, value: any, callingMethod: string, requestIdPrefix?: string): Promise { + async rPush(key: string, value: any, callingMethod: string, requestDetails: RequestDetails): Promise { const client = await this.getConnectedClient(); const serializedValue = JSON.stringify(value); const result = await client.rPush(key, serializedValue); - this.logger.trace(`${requestIdPrefix} pushing ${serializedValue} to ${key} on ${callingMethod} call`); + this.logger.trace( + `${requestDetails.formattedRequestId} pushing ${serializedValue} to ${key} on ${callingMethod} call`, + ); return result; } @@ -355,13 +372,15 @@ export class RedisCache implements IRedisCacheClient { * Retrieves all keys matching a pattern. * @param {string} pattern The pattern to match * @param {string} callingMethod The name of the calling method - * @param {string} [requestIdPrefix] The optional request ID prefix + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The list of keys matching the pattern */ - async keys(pattern: string, callingMethod: string, requestIdPrefix?: string): Promise { + async keys(pattern: string, callingMethod: string, requestDetails: RequestDetails): Promise { const client = await this.getConnectedClient(); const result = await client.keys(pattern); - this.logger.trace(`${requestIdPrefix} retrieving keys matching ${pattern} on ${callingMethod} call`); + this.logger.trace( + `${requestDetails.formattedRequestId} retrieving keys matching ${pattern} on ${callingMethod} call`, + ); return result; } } diff --git a/packages/relay/src/lib/clients/mirrorNodeClient.ts b/packages/relay/src/lib/clients/mirrorNodeClient.ts index 244347b2b5..fdef1c5e9a 100644 --- a/packages/relay/src/lib/clients/mirrorNodeClient.ts +++ b/packages/relay/src/lib/clients/mirrorNodeClient.ts @@ -42,6 +42,7 @@ import { IContractLogsResultsParams, MirrorNodeTransactionRecord, IMirrorNodeTransactionRecord, + RequestDetails, } from '../types'; type REQUEST_METHODS = 'GET' | 'POST'; @@ -121,9 +122,6 @@ export class MirrorNodeClient { private static readonly FORWARD_SLASH = '/'; private static readonly HTTPS_PREFIX = 'https://'; private static readonly API_V1_POST_FIX = 'api/v1/'; - private static readonly EMPTY_STRING = ''; - private static readonly REQUEST_PREFIX_SEPARATOR = ': '; - private static readonly REQUEST_PREFIX_TRAILING_BRACKET = ']'; private static readonly HTTP_GET = 'GET'; private static readonly REQUESTID_LABEL = 'requestId'; @@ -166,7 +164,7 @@ export class MirrorNodeClient { // defualt values for axios clients to mirror node const mirrorNodeTimeout = parseInt(process.env.MIRROR_NODE_TIMEOUT || '10000'); const mirrorNodeMaxRedirects = parseInt(process.env.MIRROR_NODE_MAX_REDIRECTS || '5'); - const mirrorNodeHttpKeepAlive = process.env.MIRROR_NODE_HTTP_KEEP_ALIVE === 'false' ? false : true; + const mirrorNodeHttpKeepAlive = process.env.MIRROR_NODE_HTTP_KEEP_ALIVE !== 'false'; const mirrorNodeHttpKeepAliveMsecs = parseInt(process.env.MIRROR_NODE_HTTP_KEEP_ALIVE_MSECS || '1000'); const mirrorNodeHttpMaxSockets = parseInt(process.env.MIRROR_NODE_HTTP_MAX_SOCKETS || '300'); const mirrorNodeHttpMaxTotalSockets = parseInt(process.env.MIRROR_NODE_HTTP_MAX_TOTAL_SOCKETS || '300'); @@ -180,7 +178,7 @@ export class MirrorNodeClient { ? JSON.parse(process.env.MIRROR_NODE_RETRY_CODES) : []; // we are in the process of deprecating this feature // by default will be true, unless explicitly set to false. - const useCacheableDnsLookup: boolean = process.env.MIRROR_NODE_AGENT_CACHEABLE_DNS === 'false' ? false : true; + const useCacheableDnsLookup: boolean = process.env.MIRROR_NODE_AGENT_CACHEABLE_DNS !== 'false'; const httpAgent = new http.Agent({ keepAlive: mirrorNodeHttpKeepAlive, @@ -313,22 +311,16 @@ export class MirrorNodeClient { path: string, pathLabel: string, method: REQUEST_METHODS, + requestDetails: RequestDetails, data?: any, - requestIdPrefix?: string, retries?: number, ): Promise { 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; const controller = new AbortController(); try { const axiosRequestConfig: AxiosRequestConfig = { headers: { - [MirrorNodeClient.REQUESTID_LABEL]: requestId, + [MirrorNodeClient.REQUESTID_LABEL]: requestDetails.requestId, }, signal: controller.signal, }; @@ -350,7 +342,7 @@ export class MirrorNodeClient { } const ms = Date.now() - start; - this.logger.debug(`${requestId} [${method}] ${path} ${response.status} ${ms} ms`); + this.logger.debug(`${requestDetails.formattedRequestId} [${method}] ${path} ${response.status} ${ms} ms`); this.mirrorResponseHistogram.labels(pathLabel, response.status?.toString()).observe(ms); return response.data; } catch (error: any) { @@ -364,25 +356,30 @@ export class MirrorNodeClient { // always abort the request on failure as the axios call can hang until the parent code/stack times out (might be a few minutes in a server-side applications) controller.abort(); - this.handleError(error, path, pathLabel, effectiveStatusCode, method, requestIdPrefix); + this.handleError(error, path, pathLabel, effectiveStatusCode, method, requestDetails); } return null; } - async get(path: string, pathLabel: string, requestIdPrefix?: string, retries?: number): Promise { - return this.request(path, pathLabel, 'GET', null, requestIdPrefix, retries); + async get( + path: string, + pathLabel: string, + requestDetails: RequestDetails, + retries?: number, + ): Promise { + return this.request(path, pathLabel, 'GET', requestDetails, null, retries); } async post( path: string, data: any, pathLabel: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, retries?: number, ): Promise { if (!data) data = {}; - return this.request(path, pathLabel, 'POST', data, requestIdPrefix, retries); + return this.request(path, pathLabel, 'POST', requestDetails, data, retries); } /** @@ -395,8 +392,9 @@ export class MirrorNodeClient { pathLabel: string, effectiveStatusCode: number, method: REQUEST_METHODS, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): null { + const requestIdPrefix = requestDetails.formattedRequestId; const mirrorError = new MirrorNodeClientError(error, effectiveStatusCode); const acceptedErrorResponses = MirrorNodeClient.acceptedErrorStatusesResponsePerRequestPathMap.get(pathLabel); @@ -426,12 +424,12 @@ export class MirrorNodeClient { url: string, pathLabel: string, resultProperty: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, results = [], page = 1, pageMax: number = constants.MAX_MIRROR_NODE_PAGINATION, ) { - const result = await this.get(url, pathLabel, requestIdPrefix); + const result = await this.get(url, pathLabel, requestDetails); if (result && result[resultProperty]) { results = results.concat(result[resultProperty]); @@ -439,24 +437,26 @@ export class MirrorNodeClient { if (page === pageMax) { // max page reached - this.logger.trace(`${requestIdPrefix} Max page reached ${pageMax} with ${results.length} results`); + this.logger.trace( + `${requestDetails.formattedRequestId} Max page reached ${pageMax} with ${results.length} results`, + ); throw predefined.PAGINATION_MAX(pageMax); } if (result?.links?.next && page < pageMax) { page++; const next = result.links.next.replace(constants.NEXT_LINK_PREFIX, ''); - return this.getPaginatedResults(next, pathLabel, resultProperty, requestIdPrefix, results, page, pageMax); + return this.getPaginatedResults(next, pathLabel, resultProperty, requestDetails, results, page, pageMax); } else { return results; } } - public async getAccount(idOrAliasOrEvmAddress: string, requestIdPrefix?: string, retries?: number) { + public async getAccount(idOrAliasOrEvmAddress: string, requestDetails: RequestDetails, retries?: number) { return this.get( `${MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT}${idOrAliasOrEvmAddress}?transactions=false`, MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT, - requestIdPrefix, + requestDetails, retries, ); } @@ -464,8 +464,8 @@ export class MirrorNodeClient { public async getAccountLatestEthereumTransactionsByTimestamp( idOrAliasOrEvmAddress: string, timestampTo: string, + requestDetails: RequestDetails, numberOfTransactions: number = 1, - requestIdPrefix?: string, ) { const queryParamObject = {}; this.setQueryParam( @@ -483,24 +483,24 @@ export class MirrorNodeClient { return this.get( `${MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT}${idOrAliasOrEvmAddress}${queryParams}`, MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT, - requestIdPrefix, + requestDetails, ); } - public async getAccountPageLimit(idOrAliasOrEvmAddress: string, requestIdPrefix?: string) { + public async getAccountPageLimit(idOrAliasOrEvmAddress: string, requestDetails: RequestDetails) { return this.get( `${MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT}${idOrAliasOrEvmAddress}?limit=${constants.MIRROR_NODE_QUERY_LIMIT}`, MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT, - requestIdPrefix, + requestDetails, ); } /******************************************************************************* * To be used to make paginated calls for the account information when the * transaction count exceeds the constant MIRROR_NODE_QUERY_LIMIT. *******************************************************************************/ - public async getAccountPaginated(url: string, requestIdPrefix?: string) { + public async getAccountPaginated(url: string, requestDetails: RequestDetails) { const queryParamObject = {}; - const accountId = this.extractAccountIdFromUrl(url, requestIdPrefix); + const accountId = this.extractAccountIdFromUrl(url, requestDetails); const params = new URLSearchParams(url.split('?')[1]); this.setQueryParam(queryParamObject, 'limit', constants.MIRROR_NODE_QUERY_LIMIT); @@ -511,11 +511,11 @@ export class MirrorNodeClient { `${MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT}${accountId}${queryParams}`, MirrorNodeClient.GET_ACCOUNTS_BY_ID_ENDPOINT, 'transactions', - requestIdPrefix, + requestDetails, ); } - public extractAccountIdFromUrl(url: string, requestIdPrefix?: string): string | null { + public extractAccountIdFromUrl(url: string, requestDetails: RequestDetails): string | null { const substringStartIndex = url.indexOf('/accounts/') + '/accounts/'.length; if (url.startsWith('0x', substringStartIndex)) { // evm addresss @@ -523,7 +523,7 @@ export class MirrorNodeClient { const match = url.match(regex); const accountId = match ? match[1] : null; if (!accountId) { - this.logger.error(`${requestIdPrefix} Unable to extract evm address from url ${url}`); + this.logger.error(`${requestDetails.formattedRequestId} Unable to extract evm address from url ${url}`); } return String(accountId); } else { @@ -531,7 +531,7 @@ export class MirrorNodeClient { const match = url.match(MirrorNodeClient.EVM_ADDRESS_REGEX); const accountId = match ? match[1] : null; if (!accountId) { - this.logger.error(`${requestIdPrefix} Unable to extract account ID from url ${url}`); + this.logger.error(`${requestDetails.formattedRequestId} Unable to extract account ID from url ${url}`); } return String(accountId); } @@ -541,7 +541,7 @@ export class MirrorNodeClient { accountId: string, timestampFrom: string, timestampTo: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ) { const queryParamObject = {}; this.setQueryParam(queryParamObject, 'account.id', accountId); @@ -553,11 +553,11 @@ export class MirrorNodeClient { `${MirrorNodeClient.GET_TRANSACTIONS_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_TRANSACTIONS_ENDPOINT, 'transactions', - requestIdPrefix, + requestDetails, ); } - public async getBalanceAtTimestamp(accountId: string, timestamp?: string, requestIdPrefix?: string) { + public async getBalanceAtTimestamp(accountId: string, requestDetails: RequestDetails, timestamp?: string) { const queryParamObject = {}; this.setQueryParam(queryParamObject, 'account.id', accountId); this.setQueryParam(queryParamObject, 'timestamp', timestamp); @@ -565,16 +565,16 @@ export class MirrorNodeClient { return this.get( `${MirrorNodeClient.GET_BALANCE_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_BALANCE_ENDPOINT, - requestIdPrefix, + requestDetails, ); } - public async getBlock(hashOrBlockNumber: string | number, requestIdPrefix?: string) { + public async getBlock(hashOrBlockNumber: string | number, requestDetails: RequestDetails) { const cachedLabel = `${constants.CACHE_KEY.GET_BLOCK}.${hashOrBlockNumber}`; const cachedResponse: any = await this.cacheService.getAsync( cachedLabel, MirrorNodeClient.GET_BLOCK_ENDPOINT, - requestIdPrefix, + requestDetails, ); if (cachedResponse) { return cachedResponse; @@ -583,18 +583,18 @@ export class MirrorNodeClient { const block = await this.get( `${MirrorNodeClient.GET_BLOCK_ENDPOINT}${hashOrBlockNumber}`, MirrorNodeClient.GET_BLOCK_ENDPOINT, - requestIdPrefix, + requestDetails, ); - await this.cacheService.set(cachedLabel, block, MirrorNodeClient.GET_BLOCK_ENDPOINT, undefined, requestIdPrefix); + await this.cacheService.set(cachedLabel, block, MirrorNodeClient.GET_BLOCK_ENDPOINT, requestDetails); return block; } public async getBlocks( + requestDetails: RequestDetails, blockNumber?: number | string[], timestamp?: string, limitOrderParams?: ILimitOrderParams, - requestIdPrefix?: string, ) { const queryParamObject = {}; this.setQueryParam(queryParamObject, 'block.number', blockNumber); @@ -604,35 +604,35 @@ export class MirrorNodeClient { return this.get( `${MirrorNodeClient.GET_BLOCKS_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_BLOCKS_ENDPOINT, - requestIdPrefix, + requestDetails, ); } - public async getContract(contractIdOrAddress: string, requestIdPrefix?: string, retries?: number) { + public async getContract(contractIdOrAddress: string, requestDetails: RequestDetails, retries?: number) { return this.get( `${MirrorNodeClient.GET_CONTRACT_ENDPOINT}${contractIdOrAddress}`, MirrorNodeClient.GET_CONTRACT_ENDPOINT, - requestIdPrefix, + requestDetails, retries, ); } - public getIsValidContractCacheLabel(contractIdOrAddress): string { + public getIsValidContractCacheLabel(contractIdOrAddress: string): string { return `${constants.CACHE_KEY.GET_CONTRACT}.valid.${contractIdOrAddress}`; } - public async getIsValidContractCache(contractIdOrAddress, requestIdPrefix?: string): Promise { + public async getIsValidContractCache(contractIdOrAddress: string, requestDetails: RequestDetails): Promise { const cachedLabel = this.getIsValidContractCacheLabel(contractIdOrAddress); - return await this.cacheService.getAsync(cachedLabel, MirrorNodeClient.GET_CONTRACT_ENDPOINT, requestIdPrefix); + return await this.cacheService.getAsync(cachedLabel, MirrorNodeClient.GET_CONTRACT_ENDPOINT, requestDetails); } - public async isValidContract(contractIdOrAddress: string, requestIdPrefix?: string, retries?: number) { - const cachedResponse: any = await this.getIsValidContractCache(contractIdOrAddress, requestIdPrefix); + public async isValidContract(contractIdOrAddress: string, requestDetails: RequestDetails, retries?: number) { + const cachedResponse: any = await this.getIsValidContractCache(contractIdOrAddress, requestDetails); if (cachedResponse != undefined) { return cachedResponse; } - const contract = await this.getContractId(contractIdOrAddress, requestIdPrefix, retries); + const contract = await this.getContractId(contractIdOrAddress, requestDetails, retries); const valid = contract != null; const cachedLabel = this.getIsValidContractCacheLabel(contractIdOrAddress); @@ -640,18 +640,18 @@ export class MirrorNodeClient { cachedLabel, valid, MirrorNodeClient.GET_CONTRACT_ENDPOINT, + requestDetails, constants.CACHE_TTL.ONE_DAY, - requestIdPrefix, ); return valid; } - public async getContractId(contractIdOrAddress: string, requestIdPrefix?: string, retries?: number) { + public async getContractId(contractIdOrAddress: string, requestDetails: RequestDetails, retries?: number) { const cachedLabel = `${constants.CACHE_KEY.GET_CONTRACT}.id.${contractIdOrAddress}`; const cachedResponse: any = await this.cacheService.getAsync( cachedLabel, MirrorNodeClient.GET_CONTRACT_ENDPOINT, - requestIdPrefix, + requestDetails, ); if (cachedResponse != undefined) { return cachedResponse; @@ -660,7 +660,7 @@ export class MirrorNodeClient { const contract = await this.get( `${MirrorNodeClient.GET_CONTRACT_ENDPOINT}${contractIdOrAddress}`, MirrorNodeClient.GET_CONTRACT_ENDPOINT, - requestIdPrefix, + requestDetails, retries, ); @@ -670,8 +670,8 @@ export class MirrorNodeClient { cachedLabel, id, MirrorNodeClient.GET_CONTRACT_ENDPOINT, + requestDetails, constants.CACHE_TTL.ONE_DAY, - requestIdPrefix, ); return id; } @@ -679,9 +679,13 @@ export class MirrorNodeClient { return null; } - public async getContractResult(transactionIdOrHash: string, requestIdPrefix?: string) { + public async getContractResult(transactionIdOrHash: string, requestDetails: RequestDetails) { const cacheKey = `${constants.CACHE_KEY.GET_CONTRACT_RESULT}.${transactionIdOrHash}`; - const cachedResponse = await this.cacheService.getAsync(cacheKey, MirrorNodeClient.GET_CONTRACT_RESULT_ENDPOINT); + const cachedResponse = await this.cacheService.getAsync( + cacheKey, + MirrorNodeClient.GET_CONTRACT_RESULT_ENDPOINT, + requestDetails, + ); if (cachedResponse) { return cachedResponse; @@ -690,7 +694,7 @@ export class MirrorNodeClient { const response = await this.get( `${MirrorNodeClient.GET_CONTRACT_RESULT_ENDPOINT}${transactionIdOrHash}`, MirrorNodeClient.GET_CONTRACT_RESULT_ENDPOINT, - requestIdPrefix, + requestDetails, ); if ( @@ -703,8 +707,8 @@ export class MirrorNodeClient { cacheKey, response, MirrorNodeClient.GET_CONTRACT_RESULT_ENDPOINT, + requestDetails, constants.CACHE_TTL.ONE_HOUR, - requestIdPrefix, ); } @@ -715,21 +719,21 @@ export class MirrorNodeClient { * In some very rare cases the /contracts/results api is called before all the data is saved in * 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 requestIdPrefix + * @param {string} transactionIdOrHash - The transaction ID or hash + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - public async getContractResultWithRetry(transactionIdOrHash: string, requestIdPrefix?: string) { - const contractResult = await this.getContractResult(transactionIdOrHash, requestIdPrefix); + public async getContractResultWithRetry(transactionIdOrHash: string, requestDetails: RequestDetails) { + const contractResult = await this.getContractResult(transactionIdOrHash, requestDetails); if (contractResult && !(contractResult.transaction_index && contractResult.block_number)) { - return this.getContractResult(transactionIdOrHash, requestIdPrefix); + return this.getContractResult(transactionIdOrHash, requestDetails); } return contractResult; } public async getContractResults( + requestDetails: RequestDetails, contractResultsParams?: IContractResultsParams, limitOrderParams?: ILimitOrderParams, - requestIdPrefix?: string, ) { const queryParamObject = {}; this.setContractResultsParams(queryParamObject, contractResultsParams); @@ -739,47 +743,47 @@ export class MirrorNodeClient { `${MirrorNodeClient.GET_CONTRACT_RESULTS_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_CONTRACT_RESULTS_ENDPOINT, 'results', - requestIdPrefix, + requestDetails, [], 1, MirrorNodeClient.mirrorNodeContractResultsPageMax, ); } - public async getContractResultsDetails(contractId: string, timestamp: string, requestIdPrefix?: string) { + public async getContractResultsDetails(contractId: string, timestamp: string, requestDetails: RequestDetails) { return this.get( `${this.getContractResultsDetailsByContractIdAndTimestamp(contractId, timestamp)}`, MirrorNodeClient.GET_CONTRACT_RESULTS_DETAILS_BY_CONTRACT_ID_ENDPOINT, - requestIdPrefix, + requestDetails, ); } - public async getContractsResultsActions(transactionIdOrHash: string, requestIdPrefix?: string): Promise { + public async getContractsResultsActions(transactionIdOrHash: string, requestDetails: RequestDetails): Promise { return this.get( `${this.getContractResultsActionsByTransactionIdPath(transactionIdOrHash)}`, MirrorNodeClient.GET_CONTRACTS_RESULTS_ACTIONS, - requestIdPrefix, + requestDetails, ); } public async getContractsResultsOpcodes( transactionIdOrHash: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, params?: { memory?: boolean; stack?: boolean; storage?: boolean }, ): Promise { const queryParams = params ? this.getQueryParams(params) : ''; return this.get( `${this.getContractResultsOpcodesByTransactionIdPath(transactionIdOrHash)}${queryParams}`, MirrorNodeClient.GET_CONTRACTS_RESULTS_OPCODES, - requestIdPrefix, + requestDetails, ); } public async getContractResultsByAddress( contractIdOrAddress: string, + requestDetails: RequestDetails, contractResultsParams?: IContractResultsParams, limitOrderParams?: ILimitOrderParams, - requestIdPrefix?: string, ) { const queryParamObject = {}; this.setContractResultsParams(queryParamObject, contractResultsParams); @@ -788,20 +792,20 @@ export class MirrorNodeClient { return this.get( `${MirrorNodeClient.getContractResultsByAddressPath(contractIdOrAddress)}${queryParams}`, MirrorNodeClient.GET_CONTRACT_RESULTS_BY_ADDRESS_ENDPOINT, - requestIdPrefix, + requestDetails, ); } public async getContractResultsByAddressAndTimestamp( contractIdOrAddress: string, timestamp: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ) { const apiPath = MirrorNodeClient.getContractResultsByAddressAndTimestampPath(contractIdOrAddress, timestamp); return this.get( apiPath, MirrorNodeClient.GET_CONTRACT_RESULTS_DETAILS_BY_ADDRESS_AND_TIMESTAMP_ENDPOINT, - requestIdPrefix, + requestDetails, ); } @@ -825,9 +829,9 @@ export class MirrorNodeClient { } public async getContractResultsLogs( + requestDetails: RequestDetails, contractLogsResultsParams?: IContractLogsResultsParams, limitOrderParams?: ILimitOrderParams, - requestIdPrefix?: string, ) { const queryParams = this.prepareLogsParams(contractLogsResultsParams, limitOrderParams); @@ -835,7 +839,7 @@ export class MirrorNodeClient { `${MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_ENDPOINT, MirrorNodeClient.CONTRACT_RESULT_LOGS_PROPERTY, - requestIdPrefix, + requestDetails, [], 1, MirrorNodeClient.mirrorNodeContractResultsLogsPageMax, @@ -844,9 +848,9 @@ export class MirrorNodeClient { public async getContractResultsLogsByAddress( address: string, + requestDetails: RequestDetails, contractLogsResultsParams?: IContractLogsResultsParams, limitOrderParams?: ILimitOrderParams, - requestIdPrefix?: string, ) { if (address === ethers.ZeroAddress) return []; @@ -860,29 +864,29 @@ export class MirrorNodeClient { `${apiEndpoint}${queryParams}`, MirrorNodeClient.GET_CONTRACT_RESULT_LOGS_BY_ADDRESS_ENDPOINT, MirrorNodeClient.CONTRACT_RESULT_LOGS_PROPERTY, - requestIdPrefix, + requestDetails, [], 1, MirrorNodeClient.mirrorNodeContractResultsLogsPageMax, ); } - public async getEarliestBlock(requestId?: string) { + public async getEarliestBlock(requestDetails: RequestDetails) { const cachedLabel = `${constants.CACHE_KEY.GET_BLOCK}.earliest`; const cachedResponse: any = await this.cacheService.getAsync( cachedLabel, MirrorNodeClient.GET_BLOCKS_ENDPOINT, - requestId, + requestDetails, ); if (cachedResponse != undefined) { return cachedResponse; } const blocks = await this.getBlocks( + requestDetails, undefined, undefined, this.getLimitOrderQueryParam(1, MirrorNodeClient.ORDER.ASC), - requestId, ); if (blocks && blocks.blocks.length > 0) { const block = blocks.blocks[0]; @@ -890,8 +894,8 @@ export class MirrorNodeClient { cachedLabel, block, MirrorNodeClient.GET_BLOCKS_ENDPOINT, + requestDetails, constants.CACHE_TTL.ONE_DAY, - requestId, ); return block; } @@ -899,12 +903,12 @@ export class MirrorNodeClient { return null; } - public async getLatestBlock(requestIdPrefix?: string) { + public async getLatestBlock(requestDetails: RequestDetails) { return this.getBlocks( + requestDetails, undefined, undefined, this.getLimitOrderQueryParam(1, MirrorNodeClient.ORDER.DESC), - requestIdPrefix, ); } @@ -912,19 +916,18 @@ export class MirrorNodeClient { return { limit: limit, order: order }; } - public async getNetworkExchangeRate(requestId: string, timestamp?: string) { - const formattedRequestId = formatRequestIdMessage(requestId); + public async getNetworkExchangeRate(requestDetails: RequestDetails, timestamp?: string) { const queryParamObject = {}; this.setQueryParam(queryParamObject, 'timestamp', timestamp); const queryParams = this.getQueryParams(queryParamObject); return this.get( `${MirrorNodeClient.GET_NETWORK_EXCHANGERATE_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_NETWORK_EXCHANGERATE_ENDPOINT, - formattedRequestId, + requestDetails, ); } - public async getNetworkFees(timestamp?: string, order?: string, requestIdPrefix?: string) { + public async getNetworkFees(requestDetails: RequestDetails, timestamp?: string, order?: string) { const queryParamObject = {}; this.setQueryParam(queryParamObject, 'timestamp', timestamp); this.setQueryParam(queryParamObject, 'order', order); @@ -932,7 +935,7 @@ export class MirrorNodeClient { return this.get( `${MirrorNodeClient.GET_NETWORK_FEES_ENDPOINT}${queryParams}`, MirrorNodeClient.GET_NETWORK_FEES_ENDPOINT, - requestIdPrefix, + requestDetails, ); } @@ -971,11 +974,11 @@ export class MirrorNodeClient { ); } - public async getTokenById(tokenId: string, requestIdPrefix?: string, retries?: number) { + public async getTokenById(tokenId: string, requestDetails: RequestDetails, retries?: number) { return this.get( `${MirrorNodeClient.GET_TOKENS_ENDPOINT}/${tokenId}`, MirrorNodeClient.GET_TOKENS_ENDPOINT, - requestIdPrefix, + requestDetails, retries, ); } @@ -984,21 +987,21 @@ export class MirrorNodeClient { address: string, blockEndTimestamp: string | undefined, limit: number, - requestIdPrefix?: string, + requestDetails: RequestDetails, ) { // retrieve the timestamp of the contract const contractResultsParams: IContractResultsParams = blockEndTimestamp ? { timestamp: `lte:${blockEndTimestamp}` } : {}; const limitOrderParams: ILimitOrderParams = this.getLimitOrderQueryParam(limit, 'desc'); - return this.getContractResultsByAddress(address, contractResultsParams, limitOrderParams, requestIdPrefix); + return this.getContractResultsByAddress(address, requestDetails, contractResultsParams, limitOrderParams); } public async getContractStateByAddressAndSlot( address: string, slot: string, + requestDetails: RequestDetails, blockEndTimestamp?: string, - requestIdPrefix?: string, ) { const limitOrderParams: ILimitOrderParams = this.getLimitOrderQueryParam( constants.MIRROR_NODE_QUERY_LIMIT, @@ -1016,28 +1019,28 @@ export class MirrorNodeClient { MirrorNodeClient.ADDRESS_PLACEHOLDER, address, ); - return this.get(`${apiEndpoint}${queryParams}`, MirrorNodeClient.CONTRACT_ADDRESS_STATE_ENDPOINT, requestIdPrefix); + return this.get(`${apiEndpoint}${queryParams}`, MirrorNodeClient.CONTRACT_ADDRESS_STATE_ENDPOINT, requestDetails); } /** * Send a contract call request to mirror node * @param callData {IContractCallRequest} contract call request data - * @param requestIdPrefix {string} optional request id prefix + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ public async postContractCall( callData: IContractCallRequest, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { return this.post( MirrorNodeClient.CONTRACT_CALL_ENDPOINT, callData, MirrorNodeClient.CONTRACT_CALL_ENDPOINT, - requestIdPrefix, + requestDetails, 1, // historical blocks might need 1 retry due to possible timeout from mirror node ); } - public async getTransactionById(transactionId: string, nonce: number | undefined, requestIdPrefix?: string) { + public async getTransactionById(transactionId: string, requestDetails: RequestDetails, nonce?: number) { const formattedId = formatTransactionId(transactionId); if (formattedId == null) { return formattedId; @@ -1052,31 +1055,34 @@ export class MirrorNodeClient { return this.get( `${apiEndpoint}${queryParams}`, MirrorNodeClient.GET_TRANSACTIONS_ENDPOINT_TRANSACTION_ID, - requestIdPrefix, + requestDetails, ); } /** * Check if transaction fail is because of contract revert and try to fetch and log the reason. * - * @param e - * @param requestIdPrefix + * @param e - The error object. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - public async getContractRevertReasonFromTransaction(e: any, requestIdPrefix: string): Promise { + public async getContractRevertReasonFromTransaction( + e: any, + requestDetails: RequestDetails, + ): Promise { if (e instanceof SDKClientError && e.isContractRevertExecuted()) { const transactionId = e.message.match(constants.TRANSACTION_ID_REGEX); if (transactionId) { - const tx = await this.getTransactionById(transactionId[0], undefined, requestIdPrefix); + const tx = await this.getTransactionById(transactionId[0], requestDetails); if (tx === null) { - this.logger.error(`${requestIdPrefix} Transaction failed with null result`); + this.logger.error(`${requestDetails.formattedRequestId} Transaction failed with null result`); return null; } else if (tx.length === 0) { - this.logger.error(`${requestIdPrefix} Transaction failed with empty result`); + this.logger.error(`${requestDetails.formattedRequestId} Transaction failed with empty result`); return null; } else if (tx?.transactions.length > 1) { const result = tx.transactions[1].result; - this.logger.error(`${requestIdPrefix} Transaction failed with result: ${result}`); + this.logger.error(`${requestDetails.formattedRequestId} Transaction failed with result: ${result}`); return result; } } @@ -1136,22 +1142,22 @@ export class MirrorNodeClient { * @param entityIdentifier the address of the contract * @param searchableTypes the types to search for * @param callerName calling method name - * @param requestIdPrefix the request id prefix message + * @param requestDetails The request details for logging and tracking. * @param retries the number of retries * @returns entity object or null if not found */ public async resolveEntityType( entityIdentifier: string, - searchableTypes: any[] = [constants.TYPE_CONTRACT, constants.TYPE_ACCOUNT, constants.TYPE_TOKEN], callerName: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, + searchableTypes: any[] = [constants.TYPE_CONTRACT, constants.TYPE_ACCOUNT, constants.TYPE_TOKEN], retries?: number, ) { const cachedLabel = `${constants.CACHE_KEY.RESOLVE_ENTITY_TYPE}_${entityIdentifier}`; const cachedResponse: { type: string; entity: any } | undefined = await this.cacheService.getAsync( cachedLabel, callerName, - requestIdPrefix, + requestDetails, ); if (cachedResponse) { return cachedResponse; @@ -1166,7 +1172,7 @@ export class MirrorNodeClient { ); if (searchableTypes.find((t) => t === constants.TYPE_CONTRACT)) { - const contract = await this.getContract(entityIdentifier, requestIdPrefix, retries).catch(() => { + const contract = await this.getContract(entityIdentifier, requestDetails, retries).catch(() => { return null; }); if (contract) { @@ -1174,7 +1180,7 @@ export class MirrorNodeClient { type: constants.TYPE_CONTRACT, entity: contract, }; - await this.cacheService.set(cachedLabel, response, callerName, undefined, requestIdPrefix); + await this.cacheService.set(cachedLabel, response, callerName, requestDetails); return response; } } @@ -1184,7 +1190,7 @@ export class MirrorNodeClient { const promises = [ searchableTypes.find((t) => t === constants.TYPE_ACCOUNT) ? buildPromise( - this.getAccount(entityIdentifier, requestIdPrefix, retries).catch(() => { + this.getAccount(entityIdentifier, requestDetails, retries).catch(() => { return null; }), ) @@ -1196,7 +1202,7 @@ export class MirrorNodeClient { promises.push( searchableTypes.find((t) => t === constants.TYPE_TOKEN) ? buildPromise( - this.getTokenById(`0.0.${parseInt(entityIdentifier, 16)}`, requestIdPrefix, retries).catch(() => { + this.getTokenById(`0.0.${parseInt(entityIdentifier, 16)}`, requestDetails, retries).catch(() => { return null; }), ) @@ -1229,7 +1235,7 @@ export class MirrorNodeClient { type, entity: data.value, }; - await this.cacheService.set(cachedLabel, response, callerName, undefined, requestIdPrefix); + await this.cacheService.set(cachedLabel, response, callerName, requestDetails); return response; } @@ -1253,11 +1259,11 @@ export class MirrorNodeClient { * enough time for the expected data to be propagated to the Mirror node. * It provides a way to have an extended retry logic only in specific places */ - public async repeatedRequest(methodName: string, args: any[], repeatCount: number, requestId?: string) { + public async repeatedRequest(methodName: string, args: any[], repeatCount: number, requestDetails?: RequestDetails) { let result; for (let i = 0; i < repeatCount; i++) { try { - result = await this[methodName](...args, requestId); + result = await this[methodName](...args); } catch (e: any) { // note: for some methods, it will throw 404 not found error as the record is not yet recorded in mirror-node // if error is 404, `result` would be assigned as null for it to not break out the loop. @@ -1267,7 +1273,7 @@ export class MirrorNodeClient { } else { this.logger.warn( e, - `${requestId} Error raised during polling mirror node for updated records: method=${methodName}, args=${args}`, + `${requestDetails?.formattedRequestId} Error raised during polling mirror node for updated records: method=${methodName}, args=${args}`, ); } } @@ -1277,7 +1283,7 @@ export class MirrorNodeClient { } this.logger.trace( - `${requestId} Repeating request ${methodName} with args ${JSON.stringify( + `${requestDetails?.formattedRequestId} Repeating request ${methodName} with args ${JSON.stringify( args, )} retry count ${i} of ${repeatCount}. Waiting ${this.MIRROR_NODE_RETRY_DELAY} ms before repeating request`, ); @@ -1293,20 +1299,20 @@ export class MirrorNodeClient { * * @param {string} transactionId - The ID of the transaction for which the record is being retrieved. * @param {string} callerName - The name of the caller requesting the transaction record. - * @param {string} requestId - The unique identifier for the request, used for logging and tracking. * @param {string} txConstructorName - The name of the transaction constructor associated with the transaction. * @param {string} operatorAccountId - The account ID of the operator, used to calculate transaction fees. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise<{ITransactionRecordMetric}>} - An object containing the transaction fee if available, or `undefined` if the transaction record is not found. * @throws {MirrorNodeClientError} - Throws an error if no transaction record is retrieved. */ public async getTransactionRecordMetrics( transactionId: string, callerName: string, - requestId: string, txConstructorName: string, operatorAccountId: string, + requestDetails: RequestDetails, ): Promise { - const formattedRequestId = formatRequestIdMessage(requestId); + const formattedRequestId = requestDetails.formattedRequestId; this.logger.trace( `${formattedRequestId} Get transaction record via mirror node: transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}`, @@ -1314,9 +1320,9 @@ export class MirrorNodeClient { const transactionRecords = await this.repeatedRequest( this.getTransactionById.name, - [transactionId, 0], + [transactionId, requestDetails, 0], this.MIRROR_NODE_REQUEST_RETRY_COUNT, - formattedRequestId, + requestDetails, ); if (!transactionRecords) { diff --git a/packages/relay/src/lib/clients/sdkClient.ts b/packages/relay/src/lib/clients/sdkClient.ts index 1478530eb4..dd4f714a9e 100644 --- a/packages/relay/src/lib/clients/sdkClient.ts +++ b/packages/relay/src/lib/clients/sdkClient.ts @@ -57,14 +57,14 @@ import { EventEmitter } from 'events'; import HbarLimit from '../hbarlimiter'; import constants from './../constants'; import { BigNumber } from '@hashgraph/sdk/lib/Transfer'; -import { SDKClientError } from './../errors/SDKClientError'; -import { JsonRpcError, predefined } from './../errors/JsonRpcError'; +import { SDKClientError } from '../errors/SDKClientError'; +import { JsonRpcError, predefined } from '../errors/JsonRpcError'; import { CacheService } from '../services/cacheService/cacheService'; import { formatRequestIdMessage, weibarHexToTinyBarInt } from '../../formatters'; import { ITransactionRecordMetric, IExecuteQueryEventPayload, IExecuteTransactionEventPayload } from '../types'; +import { RequestDetails } from '../types'; const _ = require('lodash'); -const LRU = require('lru-cache'); export class SDKClient { /** @@ -159,16 +159,20 @@ export class SDKClient { * * @param {string} account - The account ID to retrieve the balance for. * @param {string} callerName - The name of the caller requesting the account balance. - * @param {string} [requestId] - Optional request ID for tracking the request. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves to the account balance. */ - async getAccountBalance(account: string, callerName: string, requestId?: string): Promise { + async getAccountBalance( + account: string, + callerName: string, + requestDetails: RequestDetails, + ): Promise { return this.executeQuery( new AccountBalanceQuery().setAccountId(AccountId.fromString(account)), this.clientMain, callerName, account, - requestId, + requestDetails, ); } @@ -176,11 +180,16 @@ export class SDKClient { * Retrieves the balance of an account in tinybars. * @param {string} account - The account ID to query. * @param {string} callerName - The name of the caller for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The balance of the account in tinybars. * @throws {SDKClientError} Throws an SDK client error if the balance retrieval fails. */ - async getAccountBalanceInTinyBar(account: string, callerName: string, requestId?: string): Promise { - const balance = await this.getAccountBalance(account, callerName, requestId); + async getAccountBalanceInTinyBar( + account: string, + callerName: string, + requestDetails: RequestDetails, + ): Promise { + const balance = await this.getAccountBalance(account, callerName, requestDetails); return balance.hbars.to(HbarUnit.Tinybar); } @@ -188,12 +197,16 @@ export class SDKClient { * Retrieves the balance of an account in weiBars. * @param {string} account - The account ID to query. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The balance of the account in weiBars. * @throws {SDKClientError} Throws an SDK client error if the balance retrieval fails. */ - async getAccountBalanceInWeiBar(account: string, callerName: string, requestId?: string): Promise { - const balance = await this.getAccountBalance(account, callerName, requestId); + async getAccountBalanceInWeiBar( + account: string, + callerName: string, + requestDetails: RequestDetails, + ): Promise { + const balance = await this.getAccountBalance(account, callerName, requestDetails); return SDKClient.HbarToWeiBar(balance); } @@ -201,17 +214,17 @@ export class SDKClient { * Retrieves information about an account. * @param {string} address - The account ID to query. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The information about the account. * @throws {SDKClientError} Throws an SDK client error if the account info retrieval fails. */ - async getAccountInfo(address: string, callerName: string, requestId?: string): Promise { + async getAccountInfo(address: string, callerName: string, requestDetails: RequestDetails): Promise { return this.executeQuery( new AccountInfoQuery().setAccountId(AccountId.fromString(address)), this.clientMain, callerName, address, - requestId, + requestDetails, ); } @@ -221,7 +234,7 @@ export class SDKClient { * @param {number | Long} realm - The realm number of the contract. * @param {string} address - The address of the contract. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The bytecode of the contract. * @throws {SDKClientError} Throws an SDK client error if the bytecode retrieval fails. */ @@ -230,7 +243,7 @@ export class SDKClient { realm: number | Long, address: string, callerName: string, - requestId?: string, + requestDetails: RequestDetails, ): Promise { const contractByteCodeQuery = new ContractByteCodeQuery().setContractId( ContractId.fromEvmAddress(shard, realm, address), @@ -242,7 +255,7 @@ export class SDKClient { this.clientMain, callerName, address, - requestId, + requestDetails, ); } @@ -250,17 +263,21 @@ export class SDKClient { * Retrieves the balance of a contract. * @param {string} contract - The contract ID to query. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The balance of the contract. * @throws {SDKClientError} Throws an SDK client error if the balance retrieval fails. */ - async getContractBalance(contract: string, callerName: string, requestId?: string): Promise { + async getContractBalance( + contract: string, + callerName: string, + requestDetails: RequestDetails, + ): Promise { return this.executeQuery( new AccountBalanceQuery().setContractId(ContractId.fromString(contract)), this.clientMain, callerName, contract, - requestId, + requestDetails, ); } @@ -269,24 +286,28 @@ export class SDKClient { * Converts the balance from Hbar to weiBars using the `HbarToWeiBar` method. * @param {string} account - The account address of the contract. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The contract balance in weiBars. * @throws {SDKClientError} Throws an SDK client error if the balance retrieval fails. */ - async getContractBalanceInWeiBar(account: string, callerName: string, requestId?: string): Promise { - const balance = await this.getContractBalance(account, callerName, requestId); + async getContractBalanceInWeiBar( + account: string, + callerName: string, + requestDetails: RequestDetails, + ): Promise { + const balance = await this.getContractBalance(account, callerName, requestDetails); return SDKClient.HbarToWeiBar(balance); } /** * Retrieves the current exchange rates from a file. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The exchange rates. * @throws {SDKClientError} Throws an SDK client error if the exchange rates file retrieval or parsing fails. */ - async getExchangeRate(callerName: string, requestId?: string): Promise { - const exchangeFileBytes = await this.getFileIdBytes(constants.EXCHANGE_RATE_FILE_ID, callerName, requestId); + async getExchangeRate(callerName: string, requestDetails: RequestDetails): Promise { + const exchangeFileBytes = await this.getFileIdBytes(constants.EXCHANGE_RATE_FILE_ID, callerName, requestDetails); return ExchangeRates.fromBytes(exchangeFileBytes); } @@ -294,12 +315,12 @@ export class SDKClient { /** * Retrieves the fee schedule from a file. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The fee schedules. * @throws {SDKClientError} Throws an SDK client error if the fee schedule file retrieval or parsing fails. */ - async getFeeSchedule(callerName: string, requestId?: string): Promise { - const feeSchedulesFileBytes = await this.getFileIdBytes(constants.FEE_SCHEDULE_FILE_ID, callerName, requestId); + async getFeeSchedule(callerName: string, requestDetails: RequestDetails): Promise { + const feeSchedulesFileBytes = await this.getFileIdBytes(constants.FEE_SCHEDULE_FILE_ID, callerName, requestDetails); return FeeSchedules.fromBytes(feeSchedulesFileBytes); } @@ -310,21 +331,21 @@ export class SDKClient { * MAPI does not incur any fees, while HAPI will incur a query fee. * * @param {string} callerName - The name of the caller, used for logging purposes. - * @param {string} [requestId] - Optional request ID, used for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The gas fee in tinybars. * @throws {SDKClientError} Throws an SDK client error if the fee schedules or exchange rates are invalid. */ - async getTinyBarGasFee(callerName: string, requestId?: string): Promise { + async getTinyBarGasFee(callerName: string, requestDetails: RequestDetails): Promise { const cachedResponse: number | undefined = await this.cacheService.getAsync( constants.CACHE_KEY.GET_TINYBAR_GAS_FEE, callerName, - requestId, + requestDetails, ); if (cachedResponse) { return cachedResponse; } - const feeSchedules = await this.getFeeSchedule(callerName, requestId); + const feeSchedules = await this.getFeeSchedule(callerName, requestDetails); if (_.isNil(feeSchedules.current) || feeSchedules.current?.transactionFeeSchedule === undefined) { throw new SDKClientError({}, 'Invalid FeeSchedules proto format'); } @@ -332,16 +353,10 @@ export class SDKClient { for (const schedule of feeSchedules.current?.transactionFeeSchedule) { if (schedule.hederaFunctionality?._code === constants.ETH_FUNCTIONALITY_CODE && schedule.fees !== undefined) { // get exchange rate & convert to tiny bar - const exchangeRates = await this.getExchangeRate(callerName, requestId); + const exchangeRates = await this.getExchangeRate(callerName, requestDetails); const tinyBars = this.convertGasPriceToTinyBars(schedule.fees[0].servicedata, exchangeRates); - await this.cacheService.set( - constants.CACHE_KEY.GET_TINYBAR_GAS_FEE, - tinyBars, - callerName, - undefined, - requestId, - ); + await this.cacheService.set(constants.CACHE_KEY.GET_TINYBAR_GAS_FEE, tinyBars, callerName, requestDetails); return tinyBars; } } @@ -353,17 +368,17 @@ export class SDKClient { * Retrieves the contents of a file identified by its ID and returns them as a byte array. * @param {string} address - The file ID or address of the file to retrieve. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The contents of the file as a byte array. * @throws {SDKClientError} Throws an SDK client error if the file query fails. */ - async getFileIdBytes(address: string, callerName: string, requestId?: string): Promise { + async getFileIdBytes(address: string, callerName: string, requestDetails: RequestDetails): Promise { return this.executeQuery( new FileContentsQuery().setFileId(address), this.clientMain, callerName, address, - requestId, + requestDetails, ); } @@ -374,6 +389,7 @@ export class SDKClient { * * @param {Uint8Array} transactionBuffer - The transaction data in bytes. * @param {string} callerName - The name of the caller initiating the transaction. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @param {string} originalCallerAddress - The address of the original caller making the request. * @param {number} networkGasPriceInWeiBars - The predefined gas price of the network in weibar. * @param {number} currentNetworkExchangeRateInCents - The exchange rate in cents of the current network. @@ -384,16 +400,15 @@ export class SDKClient { async submitEthereumTransaction( transactionBuffer: Uint8Array, callerName: string, + requestDetails: RequestDetails, originalCallerAddress: string, networkGasPriceInWeiBars: number, currentNetworkExchangeRateInCents: number, - requestId: string, ): Promise<{ txResponse: TransactionResponse; fileId: FileId | null }> { const ethereumTransactionData: EthereumTransactionData = EthereumTransactionData.fromBytes(transactionBuffer); const ethereumTransaction = new EthereumTransaction(); const interactingEntity = ethereumTransactionData.toJSON()['to'].toString(); let fileId: FileId | null = null; - const requestIdPrefix = formatRequestIdMessage(requestId); // if callData's size is greater than `fileAppendChunkSize` => employ HFS to create new file to carry the rest of the contents of callData if (ethereumTransactionData.callData.length <= this.fileAppendChunkSize) { @@ -408,7 +423,7 @@ export class SDKClient { hexCallDataLength, this.fileAppendChunkSize, currentNetworkExchangeRateInCents, - requestId, + requestDetails, ); if (shouldPreemptivelyLimit) { @@ -419,13 +434,13 @@ export class SDKClient { fileId = await this.createFile( ethereumTransactionData.callData, this.clientMain, - requestId, + requestDetails, callerName, interactingEntity, originalCallerAddress, ); if (!fileId) { - throw new SDKClientError({}, `${requestIdPrefix} No fileId created for transaction. `); + throw new SDKClientError({}, `${requestDetails.formattedRequestId} No fileId created for transaction. `); } ethereumTransactionData.callData = new Uint8Array(); ethereumTransaction.setEthereumData(ethereumTransactionData.toBytes()).setCallDataFileId(fileId); @@ -442,7 +457,7 @@ export class SDKClient { ethereumTransaction, callerName, interactingEntity, - requestId, + requestDetails, true, originalCallerAddress, ), @@ -456,7 +471,7 @@ export class SDKClient { * @param {number} gas - The amount of gas to use for the contract call. * @param {string} from - The address of the sender in EVM format. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - Optional request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The result of the contract function call. * @throws {SDKClientError} Throws an SDK client error if the contract call query fails. */ @@ -466,7 +481,7 @@ export class SDKClient { gas: number, from: string, callerName: string, - requestId?: string, + requestDetails: RequestDetails, ): Promise { const contract = SDKClient.prune0x(to); const contractId = contract.startsWith('00000000000') @@ -488,7 +503,7 @@ export class SDKClient { contractCallQuery.setPaymentTransactionId(TransactionId.generate(this.clientMain.operatorAccountId)); } - return this.executeQuery(contractCallQuery, this.clientMain, callerName, to, requestId); + return this.executeQuery(contractCallQuery, this.clientMain, callerName, to, requestDetails); } /** @@ -498,7 +513,7 @@ export class SDKClient { * @param {number} gas - The amount of gas to use for the contract call. * @param {string} from - The address from which the contract call is made. * @param {string} callerName - The name of the caller for logging purposes. - * @param {string} [requestId] - The request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The result of the contract function call. * @throws {JsonRpcError} Throws an error if the error is a JSON-RPC error. * @throws {SDKClientError} Throws an SDK client error if the error is not a timeout error or if the retries are exhausted. @@ -509,21 +524,20 @@ export class SDKClient { gas: number, from: string, callerName: string, - requestId?: string, + requestDetails: RequestDetails, ): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); let retries = 0; let resp; while (parseInt(process.env.CONTRACT_QUERY_TIMEOUT_RETRIES || '1') > retries) { try { - resp = await this.submitContractCallQuery(to, data, gas, from, callerName, requestId); + resp = await this.submitContractCallQuery(to, data, gas, from, callerName, requestDetails); return resp; } catch (e: any) { const sdkClientError = new SDKClientError(e, e.message); if (sdkClientError.isTimeoutExceeded()) { const delay = retries * 1000; this.logger.trace( - `${requestIdPrefix} Contract call query failed with status ${sdkClientError.message}. Retrying again after ${delay} ms ...`, + `${requestDetails.formattedRequestId} Contract call query failed with status ${sdkClientError.message}. Retrying again after ${delay} ms ...`, ); retries++; await new Promise((r) => setTimeout(r, delay)); @@ -545,7 +559,7 @@ export class SDKClient { * @param {Client} client - The client to use for executing the query. * @param {number} maxRetries - The maximum number of retries allowed. * @param {number} currentRetry - The current retry attempt number. - * @param {string} [requestId] - The request ID for logging purposes. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise<{resp: any, cost: Hbar}>} The response of the query execution and the cost used. * @throws Will throw an error if the maximum number of retries is exceeded or if the error is not due to insufficient transaction fees. */ @@ -555,7 +569,7 @@ export class SDKClient { client: Client, maxRetries: number, currentRetry: number, - requestId?: string, + requestDetails: RequestDetails, ): Promise<{ resp: any; cost: Hbar }> { const baseMultiplier = constants.QUERY_COST_INCREMENTATION_STEP; const multiplier = Math.pow(baseMultiplier, currentRetry); @@ -569,8 +583,10 @@ export class SDKClient { const sdkClientError = new SDKClientError(e, e.message); if (maxRetries > currentRetry && sdkClientError.isInsufficientTxFee()) { const newRetry = currentRetry + 1; - this.logger.info(`${requestId} Retrying query execution with increased cost, retry number: ${newRetry}`); - return await this.increaseCostAndRetryExecution(query, baseCost, client, maxRetries, newRetry, requestId); + this.logger.info( + `${requestDetails.formattedRequestId} Retrying query execution with increased cost, retry number: ${newRetry}`, + ); + return await this.increaseCostAndRetryExecution(query, baseCost, client, maxRetries, newRetry, requestDetails); } throw e; @@ -583,19 +599,20 @@ export class SDKClient { * @param {Client} client - The Hedera client to use for the query. * @param {string} callerName - The name of the caller executing the query. * @param {string} interactingEntity - The entity interacting with the query. - * @param {string} [requestId] - The optional request ID for logging and tracking. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A promise resolving to the query response. * @throws {Error} Throws an error if the query fails or if rate limits are exceeded. + * @template T - The type of the query response. */ async executeQuery( query: Query, client: Client, callerName: string, interactingEntity: string, - requestId?: string, + requestDetails: RequestDetails, ): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); const queryConstructorName = query.constructor.name; + const requestIdPrefix = requestDetails.formattedRequestId; let queryResponse: any = null; let queryCost: number | undefined = undefined; let status: string = ''; @@ -605,7 +622,7 @@ export class SDKClient { try { if (query.paymentTransactionId) { const baseCost = await query.getCost(this.clientMain); - const res = await this.increaseCostAndRetryExecution(query, baseCost, client, 3, 0, requestId); + const res = await this.increaseCostAndRetryExecution(query, baseCost, client, 3, 0, requestDetails); queryResponse = res.resp; queryCost = res.cost.toTinybars().toNumber(); } else { @@ -648,7 +665,7 @@ export class SDKClient { gasUsed: 0, interactingEntity, status, - requestId: requestIdPrefix, + requestDetails, } as IExecuteQueryEventPayload); } } @@ -660,7 +677,7 @@ export class SDKClient { * @param {Transaction} transaction - The transaction to execute. * @param {string} callerName - The name of the caller requesting the transaction. * @param {string} interactingEntity - The entity interacting with the transaction. - * @param {string} requestId - The ID of the request. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @param {boolean} shouldThrowHbarLimit - Flag to indicate whether to check HBAR limits. * @param {string} originalCallerAddress - The address of the original caller making the request. * @returns {Promise} - A promise that resolves to the transaction response. @@ -670,11 +687,10 @@ export class SDKClient { transaction: Transaction, callerName: string, interactingEntity: string, - requestId: string, + requestDetails: RequestDetails, shouldThrowHbarLimit: boolean, originalCallerAddress: string, ): Promise { - const formattedRequestId = formatRequestIdMessage(requestId); const txConstructorName = transaction.constructor.name; let transactionId: string = ''; let transactionResponse: TransactionResponse | null = null; @@ -685,7 +701,7 @@ export class SDKClient { constants.EXECUTION_MODE.TRANSACTION, callerName, originalCallerAddress, - requestId, + requestDetails, ); if (shouldLimit) { throw predefined.HBAR_RATE_LIMIT_EXCEEDED; @@ -693,7 +709,7 @@ export class SDKClient { } try { - this.logger.info(`${formattedRequestId} Execute ${txConstructorName} transaction`); + this.logger.info(`${requestDetails.formattedRequestId} Execute ${txConstructorName} transaction`); transactionResponse = await transaction.execute(this.clientMain); transactionId = transactionResponse.transactionId.toString(); @@ -702,7 +718,7 @@ export class SDKClient { const transactionReceipt = await transactionResponse.getReceipt(this.clientMain); this.logger.info( - `${formattedRequestId} Successfully execute ${txConstructorName} transaction: transactionId=${transactionResponse.transactionId}, callerName=${callerName}, status=${transactionReceipt.status}(${transactionReceipt.status._code})`, + `${requestDetails.formattedRequestId} Successfully execute ${txConstructorName} transaction: transactionId=${transactionResponse.transactionId}, callerName=${callerName}, status=${transactionReceipt.status}(${transactionReceipt.status._code})`, ); return transactionResponse; } catch (e: any) { @@ -719,12 +735,12 @@ export class SDKClient { this.logger.warn( sdkClientError, - `${formattedRequestId} Fail to execute ${txConstructorName} transaction: transactionId=${transaction.transactionId}, callerName=${callerName}, status=${sdkClientError.status}(${sdkClientError.status._code})`, + `${requestDetails.formattedRequestId} Fail to execute ${txConstructorName} transaction: transactionId=${transaction.transactionId}, callerName=${callerName}, status=${sdkClientError.status}(${sdkClientError.status._code})`, ); if (!transactionResponse) { throw predefined.INTERNAL_ERROR( - `${formattedRequestId} Transaction execution returns a null value: transactionId=${transaction.transactionId}, callerName=${callerName}, txConstructorName=${txConstructorName}`, + `${requestDetails.formattedRequestId} Transaction execution returns a null value: transactionId=${transaction.transactionId}, callerName=${callerName}, txConstructorName=${txConstructorName}`, ); } return transactionResponse; @@ -733,7 +749,7 @@ export class SDKClient { this.eventEmitter.emit(constants.EVENTS.EXECUTE_TRANSACTION, { transactionId, callerName, - requestId, + requestDetails, txConstructorName, operatorAccountId: this.clientMain.operatorAccountId!.toString(), interactingEntity, @@ -748,7 +764,7 @@ export class SDKClient { * @param {FileAppendTransaction} transaction - The batch transaction to execute. * @param {string} callerName - The name of the caller requesting the transaction. * @param {string} interactingEntity - The entity interacting with the transaction. - * @param {string} requestId - The ID of the request. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @param {boolean} shouldThrowHbarLimit - Flag to indicate whether to check HBAR limits. * @param {string} originalCallerAddress - The address of the original caller making the request. * @returns {Promise} - A promise that resolves when the batch execution is complete. @@ -758,11 +774,10 @@ export class SDKClient { transaction: FileAppendTransaction, callerName: string, interactingEntity: string, - requestId: string, + requestDetails: RequestDetails, shouldThrowHbarLimit: boolean, originalCallerAddress: string, ): Promise { - const formattedRequestId = formatRequestIdMessage(requestId); const txConstructorName = transaction.constructor.name; let transactionResponses: TransactionResponse[] | null = null; @@ -772,7 +787,7 @@ export class SDKClient { constants.EXECUTION_MODE.TRANSACTION, callerName, originalCallerAddress, - requestId, + requestDetails, ); if (shouldLimit) { throw predefined.HBAR_RATE_LIMIT_EXCEEDED; @@ -780,17 +795,17 @@ export class SDKClient { } try { - this.logger.info(`${formattedRequestId} Execute ${txConstructorName} transaction`); + this.logger.info(`${requestDetails.formattedRequestId} Execute ${txConstructorName} transaction`); transactionResponses = await transaction.executeAll(this.clientMain); this.logger.info( - `${formattedRequestId} Successfully execute all ${transactionResponses.length} ${txConstructorName} transactions: callerName=${callerName}, status=${Status.Success}(${Status.Success._code})`, + `${requestDetails.formattedRequestId} Successfully execute all ${transactionResponses.length} ${txConstructorName} transactions: callerName=${callerName}, status=${Status.Success}(${Status.Success._code})`, ); } catch (e: any) { const sdkClientError = new SDKClientError(e, e.message); this.logger.warn( - `${formattedRequestId} Fail to executeAll for ${txConstructorName} transaction: transactionId=${transaction.transactionId}, callerName=${callerName}, transactionType=${txConstructorName}, status=${sdkClientError.status}(${sdkClientError.status._code})`, + `${requestDetails.formattedRequestId} Fail to executeAll for ${txConstructorName} transaction: transactionId=${transaction.transactionId}, callerName=${callerName}, transactionType=${txConstructorName}, status=${sdkClientError.status}(${sdkClientError.status._code})`, ); throw sdkClientError; } finally { @@ -800,7 +815,7 @@ export class SDKClient { this.eventEmitter.emit(constants.EVENTS.EXECUTE_TRANSACTION, { transactionId: transactionResponse.transactionId.toString(), callerName, - requestId, + requestDetails, txConstructorName, operatorAccountId: this.clientMain.operatorAccountId!.toString(), interactingEntity, @@ -815,7 +830,7 @@ export class SDKClient { * Creates a file on the Hedera network using the provided call data. * @param {Uint8Array} callData - The data to be written to the file. * @param {Client} client - The Hedera client to use for the transaction. - * @param {string} requestId - The request ID associated with the transaction. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @param {string} callerName - The name of the caller creating the file. * @param {string} interactingEntity - The entity interacting with the transaction. * @param {string} originalCallerAddress - The address of the original caller making the request. @@ -825,12 +840,11 @@ export class SDKClient { async createFile( callData: Uint8Array, client: Client, - requestId: string, + requestDetails: RequestDetails, callerName: string, interactingEntity: string, originalCallerAddress: string, ): Promise { - const formattedRequestId = formatRequestIdMessage(requestId); const hexedCallData = Buffer.from(callData).toString('hex'); const fileCreateTx = new FileCreateTransaction() @@ -841,7 +855,7 @@ export class SDKClient { fileCreateTx, callerName, interactingEntity, - formattedRequestId, + requestDetails, true, originalCallerAddress, ); @@ -859,7 +873,7 @@ export class SDKClient { fileAppendTx, callerName, interactingEntity, - formattedRequestId, + requestDetails, true, originalCallerAddress, ); @@ -871,14 +885,16 @@ export class SDKClient { this.clientMain, callerName, interactingEntity, - requestId, + requestDetails, ); if (fileInfo.size.isZero()) { - this.logger.warn(`${requestId} File ${fileId} is empty.`); - throw new SDKClientError({}, `${requestId} Created file is empty. `); + this.logger.warn(`${requestDetails.formattedRequestId} File ${fileId} is empty.`); + throw new SDKClientError({}, `${requestDetails.formattedRequestId} Created file is empty. `); } - this.logger.trace(`${formattedRequestId} Created file with fileId: ${fileId} and file size ${fileInfo.size}`); + this.logger.trace( + `${requestDetails.formattedRequestId} Created file with fileId: ${fileId} and file size ${fileInfo.size}`, + ); } return fileId; @@ -888,7 +904,7 @@ export class SDKClient { * Deletes a file on the Hedera network and verifies its deletion. * * @param {FileId} fileId - The ID of the file to be deleted. - * @param {string} requestId - A unique identifier for the request. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @param {string} callerName - The name of the entity initiating the request. * @param {string} interactingEntity - The name of the interacting entity. * @param {string} originalCallerAddress - The address of the original caller making the request. @@ -897,13 +913,11 @@ export class SDKClient { */ async deleteFile( fileId: FileId, - requestId: string, + requestDetails: RequestDetails, callerName: string, interactingEntity: string, originalCallerAddress: string, ): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); - try { const fileDeleteTx = new FileDeleteTransaction() .setFileId(fileId) @@ -914,7 +928,7 @@ export class SDKClient { fileDeleteTx, callerName, interactingEntity, - requestId, + requestDetails, false, originalCallerAddress, ); @@ -924,16 +938,16 @@ export class SDKClient { this.clientMain, callerName, interactingEntity, - requestId, + requestDetails, ); if (fileInfo.isDeleted) { - this.logger.trace(`${requestIdPrefix} Deleted file with fileId: ${fileId}`); + this.logger.trace(`${requestDetails.formattedRequestId} Deleted file with fileId: ${fileId}`); } else { - this.logger.warn(`${requestIdPrefix} Fail to delete file with fileId: ${fileId} `); + this.logger.warn(`${requestDetails.formattedRequestId} Fail to delete file with fileId: ${fileId} `); } } catch (error: any) { - this.logger.warn(`${requestIdPrefix} ${error['message']} `); + this.logger.warn(`${requestDetails.formattedRequestId} ${error['message']} `); } } @@ -966,26 +980,25 @@ export class SDKClient { * * @param {string} transactionId - The ID of the transaction to retrieve metrics for. * @param {string} callerName - The name of the caller requesting the transaction record. - * @param {string} requestId - The request ID for tracking the request. * @param {string} txConstructorName - The name of the transaction constructor. * @param {string} operatorAccountId - The account ID of the operator. - * @returns {Promise} - A promise that resolves to an object containing transaction metrics. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. + * @returns {Promise} - A promise that resolves to an object containing transaction metrics. * @throws {SDKClientError} - Throws an error if an issue occurs during the transaction record query. */ public async getTransactionRecordMetrics( transactionId: string, callerName: string, - requestId: string, txConstructorName: string, operatorAccountId: string, + requestDetails: RequestDetails, ): Promise { let gasUsed: number = 0; let transactionFee: number = 0; let txRecordChargeAmount: number = 0; - const formattedRequestId = formatRequestIdMessage(requestId); try { this.logger.trace( - `${formattedRequestId} Get transaction record via consensus node: transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}`, + `${requestDetails.formattedRequestId} Get transaction record via consensus node: transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}`, ); const transactionRecord = await new TransactionRecordQuery() @@ -1006,7 +1019,7 @@ export class SDKClient { const sdkClientError = new SDKClientError(e, e.message); this.logger.warn( e, - `${formattedRequestId} Error raised during TransactionRecordQuery: transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}, recordStatus=${sdkClientError.status} (${sdkClientError.status._code}), cost=${transactionFee}, gasUsed=${gasUsed}`, + `${requestDetails.formattedRequestId} Error raised during TransactionRecordQuery: transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}, recordStatus=${sdkClientError.status} (${sdkClientError.status._code}), cost=${transactionFee}, gasUsed=${gasUsed}`, ); throw sdkClientError; } diff --git a/packages/relay/src/lib/db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.ts b/packages/relay/src/lib/db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.ts index 90a90b918c..c7d39bac5e 100644 --- a/packages/relay/src/lib/db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.ts +++ b/packages/relay/src/lib/db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.ts @@ -23,6 +23,7 @@ import { Logger } from 'pino'; import { IEthAddressHbarSpendingPlan } from '../../types/hbarLimiter/ethAddressHbarSpendingPlan'; import { EthAddressHbarSpendingPlanNotFoundError } from '../../types/hbarLimiter/errors'; import { EthAddressHbarSpendingPlan } from '../../entities/hbarLimiter/ethAddressHbarSpendingPlan'; +import { RequestDetails } from '../../../types'; export class EthAddressHbarSpendingPlanRepository { private readonly collectionKey = 'ethAddressHbarSpendingPlan'; @@ -49,11 +50,12 @@ export class EthAddressHbarSpendingPlanRepository { * Finds an {@link EthAddressHbarSpendingPlan} for an ETH address. * * @param {string} ethAddress - The ETH address to search for. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - The associated plan for the ETH address. */ - async findByAddress(ethAddress: string): Promise { + async findByAddress(ethAddress: string, requestDetails: RequestDetails): Promise { const key = this.getKey(ethAddress); - const addressPlan = await this.cache.getAsync(key, 'findByAddress'); + const addressPlan = await this.cache.getAsync(key, 'findByAddress', requestDetails); if (!addressPlan) { throw new EthAddressHbarSpendingPlanNotFoundError(ethAddress); } @@ -65,11 +67,12 @@ export class EthAddressHbarSpendingPlanRepository { * Saves an {@link EthAddressHbarSpendingPlan} to the cache, linking the plan to the ETH address. * * @param {IEthAddressHbarSpendingPlan} addressPlan - The plan to save. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves when the ETH address is linked to the plan. */ - async save(addressPlan: IEthAddressHbarSpendingPlan): Promise { + async save(addressPlan: IEthAddressHbarSpendingPlan, requestDetails: RequestDetails): Promise { const key = this.getKey(addressPlan.ethAddress); - await this.cache.set(key, addressPlan, 'save', this.threeMonthsInMillis); + await this.cache.set(key, addressPlan, 'save', requestDetails, this.threeMonthsInMillis); this.logger.trace(`Saved EthAddressHbarSpendingPlan with address ${addressPlan.ethAddress}`); } @@ -77,11 +80,12 @@ export class EthAddressHbarSpendingPlanRepository { * Deletes an {@link EthAddressHbarSpendingPlan} from the cache, unlinking the plan from the ETH address. * * @param {string} ethAddress - The ETH address to unlink the plan from. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves when the ETH address is unlinked from the plan. */ - async delete(ethAddress: string): Promise { + async delete(ethAddress: string, requestDetails: RequestDetails): Promise { const key = this.getKey(ethAddress); - await this.cache.delete(key, 'delete'); + await this.cache.delete(key, 'delete', requestDetails); this.logger.trace(`Deleted EthAddressHbarSpendingPlan with address ${ethAddress}`); } diff --git a/packages/relay/src/lib/db/repositories/hbarLimiter/hbarSpendingPlanRepository.ts b/packages/relay/src/lib/db/repositories/hbarLimiter/hbarSpendingPlanRepository.ts index 3bfbac5d3c..13a9a46160 100644 --- a/packages/relay/src/lib/db/repositories/hbarLimiter/hbarSpendingPlanRepository.ts +++ b/packages/relay/src/lib/db/repositories/hbarLimiter/hbarSpendingPlanRepository.ts @@ -27,6 +27,7 @@ import { IDetailedHbarSpendingPlan, IHbarSpendingPlan } from '../../types/hbarLi import { HbarSpendingRecord } from '../../entities/hbarLimiter/hbarSpendingRecord'; import { SubscriptionType } from '../../types/hbarLimiter/subscriptionType'; import { HbarSpendingPlan } from '../../entities/hbarLimiter/hbarSpendingPlan'; +import { RequestDetails } from '../../../types'; export class HbarSpendingPlanRepository { private readonly collectionKey = 'hbarSpendingPlan'; @@ -51,13 +52,14 @@ export class HbarSpendingPlanRepository { } /** - * Gets a hbar spending plan by ID. - * @param id - The ID of the plan to get. - * @returns {Promise} - The hbar spending plan object. + * Gets an HBar spending plan by ID. + * @param {string} id - The ID of the plan to get. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. + * @returns {Promise} - The HBar spending plan object. */ - async findById(id: string): Promise { + async findById(id: string, requestDetails: RequestDetails): Promise { const key = this.getKey(id); - const plan = await this.cache.getAsync(key, 'findById'); + const plan = await this.cache.getAsync(key, 'findById', requestDetails); if (!plan) { throw new HbarSpendingPlanNotFoundError(id); } @@ -69,25 +71,27 @@ export class HbarSpendingPlanRepository { } /** - * Gets a hbar spending plan by ID with detailed information (spendingHistory and spentToday). - * @param id - The ID of the plan. - * @returns {Promise} - The detailed hbar spending plan object. + * Gets an HBar spending plan by ID with detailed information (spendingHistory and spentToday). + * @param {string} id - The ID of the plan. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. + * @returns {Promise} - The detailed HBar spending plan object. */ - async findByIdWithDetails(id: string): Promise { - const plan = await this.findById(id); + async findByIdWithDetails(id: string, requestDetails: RequestDetails): Promise { + const plan = await this.findById(id, requestDetails); return new HbarSpendingPlan({ ...plan, spendingHistory: [], - spentToday: await this.getSpentToday(id), + spentToday: await this.getSpentToday(id, requestDetails), }); } /** - * Creates a new hbar spending plan. - * @param subscriptionType - The subscription type of the plan to create. - * @returns {Promise} - The created hbar spending plan object. + * Creates a new HBar spending plan. + * @param {SubscriptionType} subscriptionType - The subscription type of the plan to create. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. + * @returns {Promise} - The created HBar spending plan object. */ - async create(subscriptionType: SubscriptionType): Promise { + async create(subscriptionType: SubscriptionType, requestDetails: RequestDetails): Promise { const plan: IDetailedHbarSpendingPlan = { id: uuidV4(randomBytes(16)), subscriptionType, @@ -98,104 +102,121 @@ export class HbarSpendingPlanRepository { }; this.logger.trace(`Creating HbarSpendingPlan with ID ${plan.id}...`); const key = this.getKey(plan.id); - await this.cache.set(key, plan, 'create', this.threeMonthsInMillis); + await this.cache.set(key, plan, 'create', requestDetails, this.threeMonthsInMillis); return new HbarSpendingPlan(plan); } /** - * Verify that an hbar spending plan exists and is active. - * @param id - The ID of the plan. + * Verify that an HBar spending plan exists and is active. + * @param {string} id - The ID of the plan. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves if the plan exists and is active, or rejects if not. */ - async checkExistsAndActive(id: string): Promise { - const plan = await this.findById(id); + async checkExistsAndActive(id: string, requestDetails: RequestDetails): Promise { + const plan = await this.findById(id, requestDetails); if (!plan.active) { throw new HbarSpendingPlanNotActiveError(id); } } /** - * Gets the spending history for a hbar spending plan. - * @param id - The ID of the plan. + * Gets the spending history for an HBar spending plan. + * @param {string} id - The ID of the plan. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the spending history. */ - async getSpendingHistory(id: string): Promise { - await this.checkExistsAndActive(id); + async getSpendingHistory(id: string, requestDetails: RequestDetails): Promise { + await this.checkExistsAndActive(id, requestDetails); this.logger.trace(`Retrieving spending history for HbarSpendingPlan with ID ${id}...`); const key = this.getSpendingHistoryKey(id); - const spendingHistory = await this.cache.lRange(key, 0, -1, 'getSpendingHistory'); + const spendingHistory = await this.cache.lRange( + key, + 0, + -1, + 'getSpendingHistory', + requestDetails, + ); return spendingHistory.map((entry) => new HbarSpendingRecord(entry)); } /** * Adds spending to a plan's spending history. - * @param id - The ID of the plan. - * @param amount - The amount to add to the plan's spending. + * @param {string} id - The ID of the plan. + * @param {number} amount - The amount to add to the plan's spending. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the new length of the spending history. */ - async addAmountToSpendingHistory(id: string, amount: number): Promise { - await this.checkExistsAndActive(id); + async addAmountToSpendingHistory(id: string, amount: number, requestDetails: RequestDetails): Promise { + await this.checkExistsAndActive(id, requestDetails); this.logger.trace(`Adding ${amount} to spending history for HbarSpendingPlan with ID ${id}...`); const key = this.getSpendingHistoryKey(id); const entry: IHbarSpendingRecord = { amount, timestamp: new Date() }; - return this.cache.rPush(key, entry, 'addAmountToSpendingHistory'); + return this.cache.rPush(key, entry, 'addAmountToSpendingHistory', requestDetails); } /** - * Gets the amount spent today for an hbar spending plan. - * @param id - The ID of the plan. + * Gets the amount spent today for an HBar spending plan. + * @param {string} id - The ID of the plan. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the amount spent today. */ - async getSpentToday(id: string): Promise { - await this.checkExistsAndActive(id); + async getSpentToday(id: string, requestDetails: RequestDetails): Promise { + await this.checkExistsAndActive(id, requestDetails); this.logger.trace(`Retrieving spentToday for HbarSpendingPlan with ID ${id}...`); const key = this.getSpentTodayKey(id); - return this.cache.getAsync(key, 'getSpentToday').then((spentToday) => parseInt(spentToday ?? '0')); + return this.cache.getAsync(key, 'getSpentToday', requestDetails).then((spentToday) => parseInt(spentToday ?? '0')); } /** * Resets the amount spent today for all hbar spending plans. * @returns {Promise} - A promise that resolves when the operation is complete. */ - async resetAllSpentTodayEntries(): Promise { + async resetAllSpentTodayEntries(requestDetails: RequestDetails): Promise { this.logger.trace('Resetting the spentToday entries for all HbarSpendingPlans...'); const callerMethod = this.resetAllSpentTodayEntries.name; - const keys = await this.cache.keys(`${this.collectionKey}:*:spentToday`, callerMethod); - await Promise.all(keys.map((key) => this.cache.delete(key, callerMethod))); + const keys = await this.cache.keys(`${this.collectionKey}:*:spentToday`, callerMethod, requestDetails); + await Promise.all(keys.map((key) => this.cache.delete(key, callerMethod, requestDetails))); this.logger.trace(`Successfully reset ${keys.length} spentToday entries for HbarSpendingPlans.`); } /** * Adds an amount to the amount spent today for a plan. - * @param id - The ID of the plan. - * @param amount - The amount to add. + * @param {string} id - The ID of the plan. + * @param {number} amount - The amount to add. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves when the operation is complete. */ - async addAmountToSpentToday(id: string, amount: number): Promise { - await this.checkExistsAndActive(id); + async addAmountToSpentToday(id: string, amount: number, requestDetails: RequestDetails): Promise { + await this.checkExistsAndActive(id, requestDetails); const key = this.getSpentTodayKey(id); - if (!(await this.cache.getAsync(key, 'addAmountToSpentToday'))) { + if (!(await this.cache.getAsync(key, 'addAmountToSpentToday', requestDetails))) { this.logger.trace(`No spending yet for HbarSpendingPlan with ID ${id}, setting spentToday to ${amount}...`); - await this.cache.set(key, amount, 'addAmountToSpentToday', this.oneDayInMillis); + await this.cache.set(key, amount, 'addAmountToSpentToday', requestDetails, this.oneDayInMillis); } else { this.logger.trace(`Adding ${amount} to spentToday for HbarSpendingPlan with ID ${id}...`); - await this.cache.incrBy(key, amount, 'addAmountToSpentToday'); + await this.cache.incrBy(key, amount, 'addAmountToSpentToday', requestDetails); } } /** - * Finds all active hbar spending plans by subscription type. + * Finds all active HBar spending plans by subscription type. * @param {SubscriptionType} subscriptionType - The subscription type to filter by. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the active spending plans. */ - async findAllActiveBySubscriptionType(subscriptionType: SubscriptionType): Promise { + async findAllActiveBySubscriptionType( + subscriptionType: SubscriptionType, + requestDetails: RequestDetails, + ): Promise { const callerMethod = this.findAllActiveBySubscriptionType.name; - const keys = await this.cache.keys(`${this.collectionKey}:*`, callerMethod); - const plans = await Promise.all(keys.map((key) => this.cache.getAsync(key, callerMethod))); + const keys = await this.cache.keys(`${this.collectionKey}:*`, callerMethod, requestDetails); + const plans = await Promise.all( + keys.map((key) => this.cache.getAsync(key, callerMethod, requestDetails)), + ); return Promise.all( plans .filter((plan) => plan.subscriptionType === subscriptionType && plan.active) @@ -205,7 +226,7 @@ export class HbarSpendingPlanRepository { ...plan, createdAt: new Date(plan.createdAt), spendingHistory: [], - spentToday: await this.getSpentToday(plan.id), + spentToday: await this.getSpentToday(plan.id, requestDetails), }), ), ); diff --git a/packages/relay/src/lib/db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.ts b/packages/relay/src/lib/db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.ts index 564cd301bf..044a99eb3d 100644 --- a/packages/relay/src/lib/db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.ts +++ b/packages/relay/src/lib/db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.ts @@ -23,6 +23,7 @@ import { Logger } from 'pino'; import { IIPAddressHbarSpendingPlan } from '../../types/hbarLimiter/ipAddressHbarSpendingPlan'; import { IPAddressHbarSpendingPlanNotFoundError } from '../../types/hbarLimiter/errors'; import { IPAddressHbarSpendingPlan } from '../../entities/hbarLimiter/ipAddressHbarSpendingPlan'; +import { RequestDetails } from '../../../types'; export class IPAddressHbarSpendingPlanRepository { private readonly collectionKey = 'ipAddressHbarSpendingPlan'; @@ -51,9 +52,9 @@ export class IPAddressHbarSpendingPlanRepository { * @param {string} ipAddress - The IP address to search for. * @returns {Promise} - The associated plan for the IP address. */ - async findByAddress(ipAddress: string): Promise { + async findByAddress(ipAddress: string, requestDetails: RequestDetails): Promise { const key = this.getKey(ipAddress); - const addressPlan = await this.cache.getAsync(key, 'findByAddress'); + const addressPlan = await this.cache.getAsync(key, 'findByAddress', requestDetails); if (!addressPlan) { throw new IPAddressHbarSpendingPlanNotFoundError(ipAddress); } @@ -67,9 +68,9 @@ export class IPAddressHbarSpendingPlanRepository { * @param {IIPAddressHbarSpendingPlan} addressPlan - The plan to save. * @returns {Promise} - A promise that resolves when the IP address is linked to the plan. */ - async save(addressPlan: IIPAddressHbarSpendingPlan): Promise { + async save(addressPlan: IIPAddressHbarSpendingPlan, requestDetails: RequestDetails): Promise { const key = this.getKey(addressPlan.ipAddress); - await this.cache.set(key, addressPlan, 'save', this.threeMonthsInMillis); + await this.cache.set(key, addressPlan, 'save', requestDetails, this.threeMonthsInMillis); this.logger.trace(`Saved IPAddressHbarSpendingPlan with address ${addressPlan.ipAddress}`); } @@ -79,9 +80,9 @@ export class IPAddressHbarSpendingPlanRepository { * @param {string} ipAddress - The IP address to unlink the plan from. * @returns {Promise} - A promise that resolves when the IP address is unlinked from the plan. */ - async delete(ipAddress: string): Promise { + async delete(ipAddress: string, requestDetails: RequestDetails): Promise { const key = this.getKey(ipAddress); - await this.cache.delete(key, 'delete'); + await this.cache.delete(key, 'delete', requestDetails); this.logger.trace(`Deleted IPAddressHbarSpendingPlan with address ${ipAddress}`); } diff --git a/packages/relay/src/lib/eth.ts b/packages/relay/src/lib/eth.ts index a612251e5e..82bd77ef7e 100644 --- a/packages/relay/src/lib/eth.ts +++ b/packages/relay/src/lib/eth.ts @@ -21,13 +21,13 @@ import crypto from 'crypto'; import { Logger } from 'pino'; import { Eth } from '../index'; -import { Utils } from './../utils'; +import { Utils } from '../utils'; import constants from './constants'; import { Precheck } from './precheck'; import { MirrorNodeClient } from './clients'; import { Counter, Registry } from 'prom-client'; import { IAccountInfo } from './types/mirrorNode'; -import { LogsBloomUtils } from './../logsBloomUtils'; +import { LogsBloomUtils } from '../logsBloomUtils'; import { DebugService } from './services/debugService'; import { SDKClientError } from './errors/SDKClientError'; import { Transaction as EthersTransaction } from 'ethers'; @@ -41,7 +41,7 @@ import { IDebugService } from './services/debugService/IDebugService'; import { MirrorNodeClientError } from './errors/MirrorNodeClientError'; import { IReceiptRootHash, ReceiptsRootUtils } from '../receiptsRootUtils'; import { IFilterService } from './services/ethService/ethFilterService/IFilterService'; -import { IFeeHistory, IContractCallRequest, IContractCallResponse, ITransactionReceipt } from './types'; +import { IFeeHistory, IContractCallRequest, IContractCallResponse, ITransactionReceipt, RequestDetails } from './types'; import { isHex, toHash32, @@ -233,17 +233,17 @@ export class EthImpl implements Eth { private readonly ethExecutionsCounter: Counter; /** - * The Common Service implemntation that contains logic shared by other services. + * The Common Service implementation that contains logic shared by other services. */ private readonly common: CommonService; /** - * The Filter Service implemntation that takes care of all filter API operations. + * The Filter Service implementation that takes care of all filter API operations. */ private readonly filterServiceImpl: FilterService; /** - * The Debug Service implemntation that takes care of all filter API operations. + * The Debug Service implementation that takes care of all filter API operations. */ private readonly debugServiceImpl: DebugService; @@ -306,8 +306,8 @@ export class EthImpl implements Eth { * This method is implemented to always return an empty array. This is in alignment * with the behavior of Infura. */ - accounts(requestIdPrefix?: string): never[] { - this.logger.trace(`${requestIdPrefix} accounts()`); + accounts(requestDetails: RequestDetails): never[] { + this.logger.trace(`${requestDetails.formattedRequestId} accounts()`); return EthImpl.accounts; } @@ -325,8 +325,9 @@ export class EthImpl implements Eth { blockCount: number, newestBlock: string, rewardPercentiles: Array | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; const maxResults = process.env.TEST === 'true' ? constants.DEFAULT_FEE_HISTORY_MAX_RESULTS @@ -337,11 +338,11 @@ export class EthImpl implements Eth { ); try { - const latestBlockNumber = await this.translateBlockTag(EthImpl.blockLatest, requestIdPrefix); + const latestBlockNumber = await this.translateBlockTag(EthImpl.blockLatest, requestDetails); const newestBlockNumber = newestBlock == EthImpl.blockLatest || newestBlock == EthImpl.blockPending ? latestBlockNumber - : await this.translateBlockTag(newestBlock, requestIdPrefix); + : await this.translateBlockTag(newestBlock, requestDetails); if (newestBlockNumber > latestBlockNumber) { return predefined.REQUEST_BEYOND_HEAD_BLOCK(newestBlockNumber, latestBlockNumber); @@ -359,14 +360,14 @@ export class EthImpl implements Eth { blockCount = 1; oldestBlock = 1; } - const gasPriceFee = await this.gasPrice(requestIdPrefix); + const gasPriceFee = await this.gasPrice(requestDetails); feeHistory = this.getRepeatedFeeHistory(blockCount, oldestBlock, rewardPercentiles, gasPriceFee); } else { // once we finish testing and refining Fixed Fee method, we can remove this else block to clean up code const cacheKey = `${constants.CACHE_KEY.FEE_HISTORY}_${blockCount}_${newestBlock}_${rewardPercentiles?.join( '', )}`; - const cachedFeeHistory = await this.cacheService.getAsync(cacheKey, EthImpl.ethFeeHistory, requestIdPrefix); + const cachedFeeHistory = await this.cacheService.getAsync(cacheKey, EthImpl.ethFeeHistory, requestDetails); if (cachedFeeHistory) { feeHistory = cachedFeeHistory; @@ -376,11 +377,11 @@ export class EthImpl implements Eth { newestBlockNumber, latestBlockNumber, rewardPercentiles, - requestIdPrefix, + requestDetails, ); } if (newestBlock != EthImpl.blockLatest && newestBlock != EthImpl.blockPending) { - await this.cacheService.set(cacheKey, feeHistory, EthImpl.ethFeeHistory, undefined, requestIdPrefix); + await this.cacheService.set(cacheKey, feeHistory, EthImpl.ethFeeHistory, requestDetails); } } @@ -391,15 +392,15 @@ export class EthImpl implements Eth { } } - private async getFeeByBlockNumber(blockNumber: number, requestIdPrefix?: string): Promise { + private async getFeeByBlockNumber(blockNumber: number, requestDetails: RequestDetails): Promise { let fee = 0; try { - const block = await this.mirrorNodeClient.getBlock(blockNumber, requestIdPrefix); - fee = await this.getFeeWeibars(EthImpl.ethFeeHistory, requestIdPrefix, `lte:${block.timestamp.to}`); + const block = await this.mirrorNodeClient.getBlock(blockNumber, requestDetails); + fee = await this.getFeeWeibars(EthImpl.ethFeeHistory, requestDetails, `lte:${block.timestamp.to}`); } catch (error) { this.logger.warn( error, - `${requestIdPrefix} Fee history cannot retrieve block or fee. Returning ${fee} fee for block ${blockNumber}`, + `${requestDetails.formattedRequestId} Fee history cannot retrieve block or fee. Returning ${fee} fee for block ${blockNumber}`, ); } @@ -436,9 +437,9 @@ export class EthImpl implements Eth { newestBlockNumber: number, latestBlockNumber: number, rewardPercentiles: Array | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { - // include newest block number in the total block count + // include the newest block number in the total block count const oldestBlockNumber = Math.max(0, newestBlockNumber - blockCount + 1); const shouldIncludeRewards = Array.isArray(rewardPercentiles) && rewardPercentiles.length > 0; const feeHistory: IFeeHistory = { @@ -449,18 +450,18 @@ export class EthImpl implements Eth { // get fees from oldest to newest blocks for (let blockNumber = oldestBlockNumber; blockNumber <= newestBlockNumber; blockNumber++) { - const fee = await this.getFeeByBlockNumber(blockNumber, requestIdPrefix); + const fee = await this.getFeeByBlockNumber(blockNumber, requestDetails); feeHistory.baseFeePerGas?.push(fee); feeHistory.gasUsedRatio?.push(EthImpl.defaultGasUsedRatio); } // get latest block fee - let nextBaseFeePerGas = _.last(feeHistory.baseFeePerGas); + let nextBaseFeePerGas: string = _.last(feeHistory.baseFeePerGas); if (latestBlockNumber > newestBlockNumber) { // get next block fee if the newest block is not the latest - nextBaseFeePerGas = await this.getFeeByBlockNumber(newestBlockNumber + 1, requestIdPrefix); + nextBaseFeePerGas = await this.getFeeByBlockNumber(newestBlockNumber + 1, requestDetails); } if (nextBaseFeePerGas) { @@ -474,24 +475,26 @@ export class EthImpl implements Eth { return feeHistory; } - private async getFeeWeibars(callerName: string, requestIdPrefix?: string, timestamp?: string): Promise { + private async getFeeWeibars(callerName: string, requestDetails: RequestDetails, timestamp?: string): Promise { let networkFees; try { - networkFees = await this.mirrorNodeClient.getNetworkFees(timestamp, undefined, requestIdPrefix); + networkFees = await this.mirrorNodeClient.getNetworkFees(requestDetails, timestamp, undefined); } catch (e: any) { this.logger.warn( e, - `${requestIdPrefix} Mirror Node threw an error while retrieving fees. Fallback to consensus node.`, + `${requestDetails.formattedRequestId} Mirror Node threw an error while retrieving fees. Fallback to consensus node.`, ); } if (_.isNil(networkFees)) { - this.logger.debug(`${requestIdPrefix} Mirror Node returned no network fees. Fallback to consensus node.`); + this.logger.debug( + `${requestDetails.formattedRequestId} Mirror Node returned no network fees. Fallback to consensus node.`, + ); networkFees = { fees: [ { - gas: await this.hapiService.getSDKClient().getTinyBarGasFee(callerName, requestIdPrefix), + gas: await this.hapiService.getSDKClient().getTinyBarGasFee(callerName, requestDetails), transaction_type: EthImpl.ethTxType, }, ], @@ -514,27 +517,27 @@ export class EthImpl implements Eth { /** * Gets the most recent block number. */ - async blockNumber(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} blockNumber()`); - return await this.common.getLatestBlockNumber(requestIdPrefix); + async blockNumber(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} blockNumber()`); + return await this.common.getLatestBlockNumber(requestDetails); } /** * Gets the most recent block number and timestamp.to which represents the block finality. */ - async blockNumberTimestamp(caller: string, requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} blockNumber()`); + async blockNumberTimestamp(caller: string, requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} blockNumber()`); const cacheKey = `${constants.CACHE_KEY.ETH_BLOCK_NUMBER}`; - const blocksResponse = await this.mirrorNodeClient.getLatestBlock(requestIdPrefix); + const blocksResponse = await this.mirrorNodeClient.getLatestBlock(requestDetails); const blocks = blocksResponse !== null ? blocksResponse.blocks : null; if (Array.isArray(blocks) && blocks.length > 0) { const currentBlock = numberTo0x(blocks[0].number); const timestamp = blocks[0].timestamp.to; const blockTimeStamp: LatestBlockNumberTimestamp = { blockNumber: currentBlock, timeStampTo: timestamp }; // save the latest block number in cache - await this.cacheService.set(cacheKey, currentBlock, caller, this.ethBlockNumberCacheTtlMs, requestIdPrefix); + await this.cacheService.set(cacheKey, currentBlock, caller, requestDetails, this.ethBlockNumberCacheTtlMs); return blockTimeStamp; } @@ -547,8 +550,8 @@ export class EthImpl implements Eth { * the same value. This can be specified via an environment variable * `CHAIN_ID`. */ - chainId(requestIdPrefix?: string): string { - this.logger.trace(`${requestIdPrefix} chainId()`); + chainId(requestDetails: RequestDetails): string { + this.logger.trace(`${requestDetails.formattedRequestId} chainId()`); return this.chain; } @@ -559,8 +562,9 @@ export class EthImpl implements Eth { async estimateGas( transaction: IContractCallRequest, _blockParam: string | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; const callData = transaction.data ? transaction.data : transaction.input; const callDataSize = callData ? callData.length : 0; @@ -575,13 +579,13 @@ export class EthImpl implements Eth { ); try { - const response = await this.estimateGasFromMirrorNode(transaction, requestIdPrefix); + const response = await this.estimateGasFromMirrorNode(transaction, requestDetails); if (response?.result) { this.logger.info(`${requestIdPrefix} Returning gas: ${response.result}`); return prepend0x(trimPrecedingZeros(response.result)); } else { this.logger.error(`${requestIdPrefix} No gas estimate returned from mirror-node: ${JSON.stringify(response)}`); - return this.predefinedGasForTransaction(transaction, requestIdPrefix); + return this.predefinedGasForTransaction(transaction, requestDetails); } } catch (e: any) { this.logger.error( @@ -591,7 +595,7 @@ export class EthImpl implements Eth { if (this.estimateGasThrows && e instanceof MirrorNodeClientError && e.isContractRevertOpcodeExecuted()) { return predefined.CONTRACT_REVERT(e.detail ?? e.message, e.data); } - return this.predefinedGasForTransaction(transaction, requestIdPrefix, e); + return this.predefinedGasForTransaction(transaction, requestDetails, e); } } @@ -599,16 +603,16 @@ export class EthImpl implements Eth { * Executes an estimate contract call gas request in the mirror node. * * @param {IContractCallRequest} transaction The transaction data for the contract call. - * @param requestIdPrefix The prefix for the request ID. + * @param {RequestDetails} requestDetails The request details for logging and tracking. * @returns {Promise} the response from the mirror node */ private async estimateGasFromMirrorNode( transaction: IContractCallRequest, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { - await this.contractCallFormat(transaction, requestIdPrefix); + await this.contractCallFormat(transaction, requestDetails); const callData = { ...transaction, estimate: true }; - return this.mirrorNodeClient.postContractCall(callData, requestIdPrefix); + return this.mirrorNodeClient.postContractCall(callData, requestDetails); } /** @@ -616,15 +620,16 @@ export class EthImpl implements Eth { * This method is used when the mirror node fails to return a gas estimate. * * @param {IContractCallRequest} transaction The transaction data for the contract call. - * @param {string} requestIdPrefix The prefix for the request ID. + * @param {RequestDetails} requestDetails The request details for logging and tracking. * @param error (Optional) received error from the mirror-node contract call request. * @returns {Promise} the calculated gas cost for the transaction */ private async predefinedGasForTransaction( transaction: IContractCallRequest, - requestIdPrefix?: string, + requestDetails: RequestDetails, error?: any, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; const isSimpleTransfer = !!transaction?.to && (!transaction.data || transaction.data === '0x'); const isContractCall = !!transaction?.to && transaction?.data && transaction.data.length >= constants.FUNCTION_SELECTOR_CHAR_LENGTH; @@ -640,7 +645,7 @@ export class EthImpl implements Eth { ); } // when account exists return default base gas - if (await this.getAccount(transaction.to!, requestIdPrefix)) { + if (await this.getAccount(transaction.to!, requestDetails)) { this.logger.warn(`${requestIdPrefix} Returning predefined gas for simple transfer: ${EthImpl.gasTxBaseCost}`); return EthImpl.gasTxBaseCost; } @@ -677,32 +682,32 @@ export class EthImpl implements Eth { * if not found, it fetches it from the mirror node. * * @param {string} address the address of the account - * @param {string} requestIdPrefix the prefix for the request ID + * @param {RequestDetails} requestDetails the request details for logging and tracking * @returns {Promise} the account (if such exists for the given address) */ - private async getAccount(address: string, requestIdPrefix?: string): Promise { + private async getAccount(address: string, requestDetails: RequestDetails): Promise { const key = `${constants.CACHE_KEY.ACCOUNT}_${address}`; - let account = await this.cacheService.getAsync(key, EthImpl.ethEstimateGas, requestIdPrefix); + let account = await this.cacheService.getAsync(key, EthImpl.ethEstimateGas, requestDetails); if (!account) { - account = await this.mirrorNodeClient.getAccount(address, requestIdPrefix); - await this.cacheService.set(key, account, EthImpl.ethEstimateGas, undefined, requestIdPrefix); + account = await this.mirrorNodeClient.getAccount(address, requestDetails); + await this.cacheService.set(key, account, EthImpl.ethEstimateGas, requestDetails); } return account; } /** * Perform value format precheck before making contract call towards the mirror node - * @param transaction - * @param requestIdPrefix + * @param {IContractCallRequest} transaction the transaction object + * @param {RequestDetails} requestDetails the request details for logging and tracking */ - async contractCallFormat(transaction: IContractCallRequest, requestIdPrefix?: string): Promise { + async contractCallFormat(transaction: IContractCallRequest, requestDetails: RequestDetails): Promise { if (transaction.value) { transaction.value = weibarHexToTinyBarInt(transaction.value); } if (transaction.gasPrice) { transaction.gasPrice = parseInt(transaction.gasPrice.toString()); } else { - transaction.gasPrice = await this.gasPrice(requestIdPrefix).then((gasPrice) => parseInt(gasPrice)); + transaction.gasPrice = await this.gasPrice(requestDetails).then((gasPrice) => parseInt(gasPrice)); } if (transaction.gas) { transaction.gas = parseInt(transaction.gas.toString()); @@ -712,7 +717,7 @@ export class EthImpl implements Eth { transaction.from = this.hapiService.getMainClientInstance().operatorPublicKey?.toEvmAddress(); } else { const operatorId = this.hapiService.getMainClientInstance().operatorAccountId!.toString(); - const operatorAccount = await this.getAccount(operatorId, requestIdPrefix); + const operatorAccount = await this.getAccount(operatorId, requestDetails); transaction.from = operatorAccount?.evm_address; } } @@ -737,159 +742,160 @@ export class EthImpl implements Eth { * @returns {Promise} The current gas price in weibars as a hexadecimal string. * @throws Will throw an error if unable to retrieve the gas price. */ - async gasPrice(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} eth_gasPrice`); + async gasPrice(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} eth_gasPrice`); try { let gasPrice: number | undefined = await this.cacheService.getAsync( constants.CACHE_KEY.GAS_PRICE, EthImpl.ethGasPrice, - requestIdPrefix, + requestDetails, ); if (!gasPrice) { - gasPrice = Utils.addPercentageBufferToGasPrice(await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix)); + gasPrice = Utils.addPercentageBufferToGasPrice(await this.getFeeWeibars(EthImpl.ethGasPrice, requestDetails)); await this.cacheService.set( constants.CACHE_KEY.GAS_PRICE, gasPrice, EthImpl.ethGasPrice, + requestDetails, this.ethGasPRiceCacheTtlMs, - requestIdPrefix, ); } return numberTo0x(gasPrice); } catch (error) { - throw this.common.genericErrorHandler(error, `${requestIdPrefix} Failed to retrieve gasPrice`); + throw this.common.genericErrorHandler(error, `${requestDetails.formattedRequestId} Failed to retrieve gasPrice`); } } /** * Gets whether this "Ethereum client" is a miner. We don't mine, so this always returns false. */ - async mining(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} mining()`); + async mining(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} mining()`); return false; } /** * TODO Needs docs, or be removed? */ - async submitWork(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} submitWork()`); + async submitWork(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} submitWork()`); return false; } /** * TODO Needs docs, or be removed? */ - async syncing(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} syncing()`); + async syncing(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} syncing()`); return false; } /** * Always returns null. There are no uncles in Hedera. */ - async getUncleByBlockHashAndIndex(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} getUncleByBlockHashAndIndex()`); + async getUncleByBlockHashAndIndex(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getUncleByBlockHashAndIndex()`); return null; } /** * Always returns null. There are no uncles in Hedera. */ - async getUncleByBlockNumberAndIndex(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} getUncleByBlockNumberAndIndex()`); + async getUncleByBlockNumberAndIndex(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getUncleByBlockNumberAndIndex()`); return null; } /** * Always returns '0x0'. There are no uncles in Hedera. */ - async getUncleCountByBlockHash(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} getUncleCountByBlockHash()`); + async getUncleCountByBlockHash(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getUncleCountByBlockHash()`); return EthImpl.zeroHex; } /** * Always returns '0x0'. There are no uncles in Hedera. */ - async getUncleCountByBlockNumber(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} getUncleCountByBlockNumber()`); + async getUncleCountByBlockNumber(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getUncleCountByBlockNumber()`); return EthImpl.zeroHex; } /** * TODO Needs docs, or be removed? */ - async hashrate(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} hashrate()`); + async hashrate(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} hashrate()`); return EthImpl.zeroHex; } /** * Always returns UNSUPPORTED_METHOD error. */ - getWork(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} getWork()`); + getWork(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} getWork()`); return predefined.UNSUPPORTED_METHOD; } /** * Unsupported methods always return UNSUPPORTED_METHOD error. */ - submitHashrate(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} submitHashrate()`); + submitHashrate(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} submitHashrate()`); return predefined.UNSUPPORTED_METHOD; } - signTransaction(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} signTransaction()`); + signTransaction(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} signTransaction()`); return predefined.UNSUPPORTED_METHOD; } - sign(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} sign()`); + sign(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} sign()`); return predefined.UNSUPPORTED_METHOD; } - sendTransaction(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} sendTransaction()`); + sendTransaction(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} sendTransaction()`); return predefined.UNSUPPORTED_METHOD; } - protocolVersion(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} protocolVersion()`); + protocolVersion(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} protocolVersion()`); return predefined.UNSUPPORTED_METHOD; } - coinbase(requestIdPrefix?: string): JsonRpcError { - this.logger.trace(`${requestIdPrefix} coinbase()`); + coinbase(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} coinbase()`); return predefined.UNSUPPORTED_METHOD; } /** * Gets the value from a storage position at the given Ethereum address. * - * @param address - * @param slot - * @param blockNumberOrTagOrHash - * @param requestIdPrefix + * @param {string} address The Ethereum address to get the storage value from + * @param {string} slot The storage slot to get the value from + * @param {RequestDetails} requestDetails The request details for logging and tracking + * @param {string | null} blockNumberOrTagOrHash The block number or tag or hash to get the storage value from */ async getStorageAt( address: string, slot: string, + requestDetails: RequestDetails, blockNumberOrTagOrHash?: string | null, - requestIdPrefix?: string, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace( `${requestIdPrefix} getStorageAt(address=${address}, slot=${slot}, blockNumberOrOrHashTag=${blockNumberOrTagOrHash})`, ); let result = EthImpl.zeroHex32Byte; // if contract or slot not found then return 32 byte 0 - const blockResponse = await this.common.getHistoricalBlockResponse(blockNumberOrTagOrHash, false, requestIdPrefix); + const blockResponse = await this.common.getHistoricalBlockResponse(requestDetails, blockNumberOrTagOrHash, false); // To save a request to the mirror node for `latest` and `pending` blocks, we directly return null from `getHistoricalBlockResponse` // But if a block number or `earliest` tag is passed and the mirror node returns `null`, we should throw an error. if (!this.common.blockTagIsLatestOrPending(blockNumberOrTagOrHash) && blockResponse == null) { @@ -899,7 +905,7 @@ export class EthImpl implements Eth { const blockEndTimestamp = blockResponse?.timestamp?.to; await this.mirrorNodeClient - .getContractStateByAddressAndSlot(address, slot, blockEndTimestamp, requestIdPrefix) + .getContractStateByAddressAndSlot(address, slot, requestDetails, blockEndTimestamp) .then((response) => { if (response !== null && response.state.length > 0) { result = response.state[0].value; @@ -928,11 +934,16 @@ export class EthImpl implements Eth { * Gets the balance of an account as of the given block from the mirror node. * Current implementation does not yet utilize blockNumber * - * @param account - * @param blockNumberOrTagOrHash - * @param requestIdPrefix + * @param {string} account The account to get the balance from + * @param {string | null} blockNumberOrTagOrHash The block number or tag or hash to get the balance from + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getBalance(account: string, blockNumberOrTagOrHash: string | null, requestIdPrefix?: string): Promise { + async getBalance( + account: string, + blockNumberOrTagOrHash: string | null, + requestDetails: RequestDetails, + ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; const latestBlockTolerance = 1; this.logger.trace(`${requestIdPrefix} getBalance(account=${account}, blockNumberOrTag=${blockNumberOrTagOrHash})`); @@ -942,18 +953,18 @@ export class EthImpl implements Eth { if (!this.common.blockTagIsLatestOrPending(blockNumberOrTagOrHash)) { let blockHashNumber, isHash; const cacheKey = `${constants.CACHE_KEY.ETH_BLOCK_NUMBER}`; - const blockNumberCached = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBalance, requestIdPrefix); + const blockNumberCached = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBalance, requestDetails); if (blockNumberCached) { this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(blockNumberCached)}`); latestBlock = { blockNumber: blockNumberCached, timeStampTo: '0' }; } else { - latestBlock = await this.blockNumberTimestamp(EthImpl.ethGetBalance, requestIdPrefix); + latestBlock = await this.blockNumberTimestamp(EthImpl.ethGetBalance, requestDetails); } if (blockNumberOrTagOrHash != null && blockNumberOrTagOrHash.length > 32) { isHash = true; - blockHashNumber = await this.mirrorNodeClient.getBlock(blockNumberOrTagOrHash); + blockHashNumber = await this.mirrorNodeClient.getBlock(blockNumberOrTagOrHash, requestDetails); } const currentBlockNumber = isHash ? Number(blockHashNumber.number) : Number(blockNumberOrTagOrHash); @@ -966,14 +977,14 @@ export class EthImpl implements Eth { // If ever we get the latest block from cache, and blockNumberOrTag is not latest, then we need to get the block timestamp // This should rarely happen. if (blockNumberOrTagOrHash !== EthImpl.blockLatest && latestBlock.timeStampTo === '0') { - latestBlock = await this.blockNumberTimestamp(EthImpl.ethGetBalance, requestIdPrefix); + latestBlock = await this.blockNumberTimestamp(EthImpl.ethGetBalance, requestDetails); } } // check cache first // create a key for the cache const cacheKey = `${constants.CACHE_KEY.ETH_GET_BALANCE}-${account}-${blockNumberOrTagOrHash}`; - let cachedBalance = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBalance, requestIdPrefix); + let cachedBalance = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBalance, requestDetails); if (cachedBalance && this.shouldUseCacheForBalance(blockNumberOrTagOrHash)) { this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(cachedBalance)}`); return cachedBalance; @@ -986,7 +997,7 @@ export class EthImpl implements Eth { try { if (!this.common.blockTagIsLatestOrPending(blockNumberOrTagOrHash)) { - const block = await this.common.getHistoricalBlockResponse(blockNumberOrTagOrHash, true, requestIdPrefix); + const block = await this.common.getHistoricalBlockResponse(requestDetails, blockNumberOrTagOrHash, true); if (block) { blockNumber = block.number; @@ -1000,8 +1011,8 @@ export class EthImpl implements Eth { if (timeDiff > constants.BALANCES_UPDATE_INTERVAL) { const balance = await this.mirrorNodeClient.getBalanceAtTimestamp( account, + requestDetails, block.timestamp.from, - requestIdPrefix, ); balanceFound = true; if (balance?.balances?.length) { @@ -1012,7 +1023,7 @@ export class EthImpl implements Eth { else { let currentBalance = 0; let balanceFromTxs = 0; - mirrorAccount = await this.mirrorNodeClient.getAccountPageLimit(account, requestIdPrefix); + mirrorAccount = await this.mirrorNodeClient.getAccountPageLimit(account, requestDetails); if (mirrorAccount) { if (mirrorAccount.balance) { currentBalance = mirrorAccount.balance.balance; @@ -1028,10 +1039,7 @@ export class EthImpl implements Eth { const nextPageTimeMarker = nextPageParams.get('timestamp'); // If nextPageTimeMarker is greater than the block.timestamp.to, then we need to paginate to get the transactions for the block.timestamp.to if (nextPageTimeMarker && nextPageTimeMarker?.split(':')[1] >= block.timestamp.to) { - const pagedTransactions = await this.mirrorNodeClient.getAccountPaginated( - nextPage, - requestIdPrefix, - ); + const pagedTransactions = await this.mirrorNodeClient.getAccountPaginated(nextPage, requestDetails); mirrorAccount.transactions = mirrorAccount.transactions.concat(pagedTransactions); } // If nextPageTimeMarker is less than the block.timestamp.to, then just run the getBalanceAtBlockTimestamp function in this case as well. @@ -1053,7 +1061,7 @@ export class EthImpl implements Eth { if (!balanceFound && !mirrorAccount) { // If no balance and no account, then we need to make a request to the mirror node for the account. - mirrorAccount = await this.mirrorNodeClient.getAccountPageLimit(account, requestIdPrefix); + mirrorAccount = await this.mirrorNodeClient.getAccountPageLimit(account, requestDetails); // Test if exists here if (mirrorAccount !== null && mirrorAccount !== undefined) { balanceFound = true; @@ -1072,12 +1080,13 @@ export class EthImpl implements Eth { // save in cache the current balance for the account and blockNumberOrTag cachedBalance = numberTo0x(weibars); + this.logger.trace('Value cached balance', cachedBalance); await this.cacheService.set( cacheKey, cachedBalance, EthImpl.ethGetBalance, + requestDetails, this.ethGetBalanceCacheTtlMs, - requestIdPrefix, ); return cachedBalance; @@ -1092,11 +1101,12 @@ export class EthImpl implements Eth { /** * Gets the smart contract code for the contract at the given Ethereum address. * - * @param address - * @param blockNumber - * @param requestIdPrefix + * @param {string} address The Ethereum address of the contract + * @param {string | null} blockNumber The block number to get the contract code from + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getCode(address: string, blockNumber: string | null, requestIdPrefix?: string): Promise { + async getCode(address: string, blockNumber: string | null, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; if (!EthImpl.isBlockParamValid(blockNumber)) { throw predefined.UNKNOWN_BLOCK( `The value passed is not a valid blockHash/blockNumber/blockTag value: ${blockNumber}`, @@ -1117,19 +1127,17 @@ export class EthImpl implements Eth { const cachedResponse: string | undefined = await this.cacheService.getAsync( cachedLabel, EthImpl.ethGetCode, - requestIdPrefix, + requestDetails, ); if (cachedResponse != undefined) { return cachedResponse; } try { - const result = await this.mirrorNodeClient.resolveEntityType( - address, - [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], - EthImpl.ethGetCode, - requestIdPrefix, - ); + const result = await this.mirrorNodeClient.resolveEntityType(address, EthImpl.ethGetCode, requestDetails, [ + constants.TYPE_CONTRACT, + constants.TYPE_TOKEN, + ]); if (result) { if (result?.type === constants.TYPE_TOKEN) { this.logger.trace(`${requestIdPrefix} Token redirect case, return redirectBytecode`); @@ -1145,8 +1153,7 @@ export class EthImpl implements Eth { cachedLabel, result?.entity.runtime_bytecode, EthImpl.ethGetCode, - undefined, - requestIdPrefix, + requestDetails, ); return result?.entity.runtime_bytecode; } @@ -1156,7 +1163,7 @@ export class EthImpl implements Eth { const bytecode = await this.hapiService .getSDKClient() - .getContractByteCode(0, 0, address, EthImpl.ethGetCode, requestIdPrefix); + .getContractByteCode(0, 0, address, EthImpl.ethGetCode, requestDetails); return prepend0x(Buffer.from(bytecode).toString('hex')); } catch (e: any) { if (e instanceof SDKClientError) { @@ -1200,20 +1207,21 @@ export class EthImpl implements Eth { /** * Gets the block with the given hash. * - * @param hash - * @param showDetails - * @param requestIdPrefix + * @param {string} hash the block hash + * @param {boolean} showDetails whether to show the details of the block + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getBlockByHash(hash: string, showDetails: boolean, requestIdPrefix?: string): Promise { + async getBlockByHash(hash: string, showDetails: boolean, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getBlockByHash(hash=${hash}, showDetails=${showDetails})`); const cacheKey = `${constants.CACHE_KEY.ETH_GET_BLOCK_BY_HASH}_${hash}_${showDetails}`; - let block = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBlockByHash, requestIdPrefix); + let block = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBlockByHash, requestDetails); if (!block) { - block = await this.getBlock(hash, showDetails, requestIdPrefix).catch((e: any) => { + block = await this.getBlock(hash, showDetails, requestDetails).catch((e: any) => { throw this.common.genericErrorHandler(e, `${requestIdPrefix} Failed to retrieve block for hash ${hash}`); }); - await this.cacheService.set(cacheKey, block, EthImpl.ethGetBlockByHash, undefined, requestIdPrefix); + await this.cacheService.set(cacheKey, block, EthImpl.ethGetBlockByHash, requestDetails); } return block; @@ -1221,17 +1229,22 @@ export class EthImpl implements Eth { /** * Gets the block by its block number. - * @param blockNumOrTag Possible values are earliest/pending/latest or hex, and can't be null (validator check). - * @param showDetails - * @param requestIdPrefix + * @param {string} blockNumOrTag Possible values are earliest/pending/latest or hex, and can't be null (validator check). + * @param {boolean} showDetails whether to show the details of the block + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getBlockByNumber(blockNumOrTag: string, showDetails: boolean, requestIdPrefix?: string): Promise { + async getBlockByNumber( + blockNumOrTag: string, + showDetails: boolean, + requestDetails: RequestDetails, + ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getBlockByNumber(blockNum=${blockNumOrTag}, showDetails=${showDetails})`); const cacheKey = `${constants.CACHE_KEY.ETH_GET_BLOCK_BY_NUMBER}_${blockNumOrTag}_${showDetails}`; - let block = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBlockByNumber, requestIdPrefix); + let block = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetBlockByNumber, requestDetails); if (!block) { - block = await this.getBlock(blockNumOrTag, showDetails, requestIdPrefix).catch((e: any) => { + block = await this.getBlock(blockNumOrTag, showDetails, requestDetails).catch((e: any) => { throw this.common.genericErrorHandler( e, `${requestIdPrefix} Failed to retrieve block for blockNum ${blockNumOrTag}`, @@ -1239,7 +1252,7 @@ export class EthImpl implements Eth { }); if (!this.common.blockTagIsLatestOrPending(blockNumOrTag)) { - await this.cacheService.set(cacheKey, block, EthImpl.ethGetBlockByNumber, undefined, requestIdPrefix); + await this.cacheService.set(cacheKey, block, EthImpl.ethGetBlockByNumber, requestDetails); } } @@ -1249,17 +1262,18 @@ export class EthImpl implements Eth { /** * Gets the number of transaction in a block by its block hash. * - * @param hash - * @param requestIdPrefix + * @param {string} hash The block hash + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getBlockTransactionCountByHash(hash: string, requestIdPrefix?: string): Promise { + async getBlockTransactionCountByHash(hash: string, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getBlockTransactionCountByHash(hash=${hash}, showDetails=%o)`); const cacheKey = `${constants.CACHE_KEY.ETH_GET_TRANSACTION_COUNT_BY_HASH}_${hash}`; const cachedResponse = await this.cacheService.getAsync( cacheKey, EthImpl.ethGetTransactionCountByHash, - requestIdPrefix, + requestDetails, ); if (cachedResponse) { this.logger.debug( @@ -1269,36 +1283,34 @@ export class EthImpl implements Eth { } const transactionCount = await this.mirrorNodeClient - .getBlock(hash, requestIdPrefix) + .getBlock(hash, requestDetails) .then((block) => EthImpl.getTransactionCountFromBlockResponse(block)) .catch((e: any) => { throw this.common.genericErrorHandler(e, `${requestIdPrefix} Failed to retrieve block for hash ${hash}`); }); - await this.cacheService.set( - cacheKey, - transactionCount, - EthImpl.ethGetTransactionCountByHash, - undefined, - requestIdPrefix, - ); + await this.cacheService.set(cacheKey, transactionCount, EthImpl.ethGetTransactionCountByHash, requestDetails); return transactionCount; } /** * Gets the number of transaction in a block by its block number. - * @param blockNumOrTag - * @param requestIdPrefix + * @param {string} blockNumOrTag Possible values are earliest/pending/latest or hex + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getBlockTransactionCountByNumber(blockNumOrTag: string, requestIdPrefix?: string): Promise { + async getBlockTransactionCountByNumber( + blockNumOrTag: string, + requestDetails: RequestDetails, + ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getBlockTransactionCountByNumber(blockNum=${blockNumOrTag}, showDetails=%o)`); - const blockNum = await this.translateBlockTag(blockNumOrTag, requestIdPrefix); + const blockNum = await this.translateBlockTag(blockNumOrTag, requestDetails); const cacheKey = `${constants.CACHE_KEY.ETH_GET_TRANSACTION_COUNT_BY_NUMBER}_${blockNum}`; const cachedResponse = await this.cacheService.getAsync( cacheKey, EthImpl.ethGetTransactionCountByNumber, - requestIdPrefix, + requestDetails, ); if (cachedResponse) { this.logger.debug( @@ -1308,7 +1320,7 @@ export class EthImpl implements Eth { } const transactionCount = await this.mirrorNodeClient - .getBlock(blockNum, requestIdPrefix) + .getBlock(blockNum, requestDetails) .then((block) => EthImpl.getTransactionCountFromBlockResponse(block)) .catch((e: any) => { throw this.common.genericErrorHandler( @@ -1317,28 +1329,23 @@ export class EthImpl implements Eth { ); }); - await this.cacheService.set( - cacheKey, - transactionCount, - EthImpl.ethGetTransactionCountByNumber, - undefined, - requestIdPrefix, - ); + await this.cacheService.set(cacheKey, transactionCount, EthImpl.ethGetTransactionCountByNumber, requestDetails); return transactionCount; } /** * Gets the transaction in a block by its block hash and transactions index. * - * @param blockHash - * @param transactionIndex - * @param requestIdPrefix + * @param {string} blockHash The block hash + * @param {string} transactionIndex The transaction index + * @param {RequestDetails} requestDetails The request details for logging and tracking */ async getTransactionByBlockHashAndIndex( blockHash: string, transactionIndex: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace( `${requestIdPrefix} getTransactionByBlockHashAndIndex(hash=${blockHash}, index=${transactionIndex})`, ); @@ -1347,7 +1354,7 @@ export class EthImpl implements Eth { return await this.getTransactionByBlockHashOrBlockNumAndIndex( { title: 'blockHash', value: blockHash }, transactionIndex, - requestIdPrefix, + requestDetails, ); } catch (error) { throw this.common.genericErrorHandler( @@ -1360,25 +1367,26 @@ export class EthImpl implements Eth { /** * Gets the transaction in a block by its block hash and transactions index. * - * @param blockNumOrTag - * @param transactionIndex - * @param requestIdPrefix + * @param {string} blockNumOrTag Possible values are earliest/pending/latest or hex + * @param {string} transactionIndex The transaction index + * @param {RequestDetails} requestDetails The request details for logging and tracking */ async getTransactionByBlockNumberAndIndex( blockNumOrTag: string, transactionIndex: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace( `${requestIdPrefix} getTransactionByBlockNumberAndIndex(blockNum=${blockNumOrTag}, index=${transactionIndex})`, ); - const blockNum = await this.translateBlockTag(blockNumOrTag, requestIdPrefix); + const blockNum = await this.translateBlockTag(blockNumOrTag, requestDetails); try { return await this.getTransactionByBlockHashOrBlockNumAndIndex( { title: 'blockNumber', value: blockNum }, transactionIndex, - requestIdPrefix, + requestDetails, ); } catch (error) { throw this.common.genericErrorHandler( @@ -1392,22 +1400,23 @@ export class EthImpl implements Eth { * Gets the number of transactions that have been executed for the given address. * This goes to the consensus nodes to determine the ethereumNonce. * - * Queries mirror node for best effort and fallsback to consensus node for contracts until HIP 729 is implemented. + * Queries mirror node for best effort and falls back to consensus node for contracts until HIP 729 is implemented. * - * @param address - * @param blockNumOrTag - * @param requestIdPrefix + * @param {string} address The account address + * @param {string | null} blockNumOrTag Possible values are earliest/pending/latest or hex + * @param {RequestDetails} requestDetails The request details for logging and tracking */ async getTransactionCount( address: string, blockNumOrTag: string | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getTransactionCount(address=${address}, blockNumOrTag=${blockNumOrTag})`); // cache considerations for high load const cacheKey = `eth_getTransactionCount_${address}_${blockNumOrTag}`; - let nonceCount = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetTransactionCount, requestIdPrefix); + let nonceCount = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetTransactionCount, requestDetails); if (nonceCount) { this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(nonceCount)}`); return nonceCount; @@ -1420,35 +1429,35 @@ export class EthImpl implements Eth { return EthImpl.zeroHex; } else if (this.common.blockTagIsLatestOrPending(blockNumOrTag)) { // if latest or pending, get latest ethereumNonce from mirror node account API - nonceCount = await this.getAccountLatestEthereumNonce(address, requestIdPrefix); + nonceCount = await this.getAccountLatestEthereumNonce(address, requestDetails); } else if (blockNumOrTag === EthImpl.blockEarliest) { - nonceCount = await this.getAccountNonceForEarliestBlock(requestIdPrefix); + nonceCount = await this.getAccountNonceForEarliestBlock(requestDetails); } else if (!isNaN(blockNum) && blockNumOrTag.length != EthImpl.blockHashLength && blockNum > 0) { - nonceCount = await this.getAccountNonceForHistoricBlock(address, blockNum, requestIdPrefix); + nonceCount = await this.getAccountNonceForHistoricBlock(address, blockNum, requestDetails); } else if (blockNumOrTag.length == EthImpl.blockHashLength && blockNumOrTag.startsWith(EthImpl.emptyHex)) { - nonceCount = await this.getAccountNonceForHistoricBlock(address, blockNumOrTag, requestIdPrefix); + nonceCount = await this.getAccountNonceForHistoricBlock(address, blockNumOrTag, requestDetails); } else { // return a '-39001: Unknown block' error per api-spec throw predefined.UNKNOWN_BLOCK(); } } else { // if no block consideration, get latest ethereumNonce from mirror node if account or from consensus node is contract until HIP 729 is implemented - nonceCount = await this.getAccountLatestEthereumNonce(address, requestIdPrefix); + nonceCount = await this.getAccountLatestEthereumNonce(address, requestDetails); } const cacheTtl = blockNumOrTag === EthImpl.blockEarliest || !isNaN(blockNum) ? constants.CACHE_TTL.ONE_DAY : this.ethGetTransactionCountCacheTtl; // cache historical values longer as they don't change - await this.cacheService.set(cacheKey, nonceCount, EthImpl.ethGetTransactionCount, cacheTtl, requestIdPrefix); + await this.cacheService.set(cacheKey, nonceCount, EthImpl.ethGetTransactionCount, requestDetails, cacheTtl); return nonceCount; } async parseRawTxAndPrecheck( transaction: string, + requestDetails: RequestDetails, networkGasPriceInWeiBars: number, - requestIdPrefix?: string, ): Promise { let interactingEntity = ''; let originatingAddress = ''; @@ -1458,14 +1467,14 @@ export class EthImpl implements Eth { interactingEntity = parsedTx.to?.toString() || ''; originatingAddress = parsedTx.from?.toString() || ''; this.logger.trace( - `${requestIdPrefix} sendRawTransaction(from=${originatingAddress}, to=${interactingEntity}, transaction=${transaction})`, + `${requestDetails.formattedRequestId} sendRawTransaction(from=${originatingAddress}, to=${interactingEntity}, transaction=${transaction})`, ); - await this.precheck.sendRawTransactionCheck(parsedTx, networkGasPriceInWeiBars, requestIdPrefix); + await this.precheck.sendRawTransactionCheck(parsedTx, networkGasPriceInWeiBars, requestDetails); return parsedTx; } catch (e: any) { this.logger.warn( - `${requestIdPrefix} Error on precheck sendRawTransaction(from=${originatingAddress}, to=${interactingEntity}, transaction=${transaction})`, + `${requestDetails.formattedRequestId} Error on precheck sendRawTransaction(from=${originatingAddress}, to=${interactingEntity}, transaction=${transaction})`, ); throw this.common.genericErrorHandler(e); } @@ -1477,11 +1486,11 @@ export class EthImpl implements Eth { transactionBuffer, txSubmitted, parsedTx, - requestIdPrefix, + requestDetails: RequestDetails, ): Promise { this.logger.error( e, - `${requestIdPrefix} Failed to successfully submit sendRawTransaction for transaction ${transaction}`, + `${requestDetails.formattedRequestId} Failed to successfully submit sendRawTransaction for transaction ${transaction}`, ); if (e instanceof JsonRpcError) { return e; @@ -1496,21 +1505,23 @@ export class EthImpl implements Eth { // running a polling loop to give mirror node enough time to update account nonce let accountNonce: number | null = null; for (let i = 0; i < mirrorNodeGetContractResultRetries; i++) { - const accountInfo = await this.mirrorNodeClient.getAccount(parsedTx.from!, requestIdPrefix); + const accountInfo = await this.mirrorNodeClient.getAccount(parsedTx.from!, requestDetails); if (accountInfo.ethereum_nonce !== parsedTx.nonce) { accountNonce = accountInfo.ethereum_nonce; break; } this.logger.trace( - `${requestIdPrefix} Repeating retry to poll for updated account nonce. Count ${i} of ${mirrorNodeGetContractResultRetries}. Waiting ${this.mirrorNodeClient.getMirrorNodeRetryDelay()} ms before initiating a new request`, + `${ + requestDetails.formattedRequestId + } Repeating retry to poll for updated account nonce. Count ${i} of ${mirrorNodeGetContractResultRetries}. Waiting ${this.mirrorNodeClient.getMirrorNodeRetryDelay()} ms before initiating a new request`, ); await new Promise((r) => setTimeout(r, this.mirrorNodeClient.getMirrorNodeRetryDelay())); } if (!accountNonce) { - this.logger.warn(`${requestIdPrefix} Cannot find updated account nonce.`); - throw predefined.INTERNAL_ERROR(`Cannot find updated account nonce for WRONG_NONCE error.`); + this.logger.warn(`${requestDetails.formattedRequestId} Cannot find updated account nonce.`); + throw predefined.INTERNAL_ERROR(`Cannot find updated account nonce for WRONT_NONCE error.`); } if (parsedTx.nonce > accountNonce) { @@ -1525,11 +1536,11 @@ export class EthImpl implements Eth { return predefined.INTERNAL_ERROR(e.message.toString()); } - await this.mirrorNodeClient.getContractRevertReasonFromTransaction(e, requestIdPrefix); + await this.mirrorNodeClient.getContractRevertReasonFromTransaction(e, requestDetails); this.logger.error( e, - `${requestIdPrefix} Failed sendRawTransaction during record retrieval for transaction ${transaction}, returning computed hash`, + `${requestDetails.formattedRequestId} Failed sendRawTransaction during record retrieval for transaction ${transaction}, returning computed hash`, ); //Return computed hash if unable to retrieve EthereumHash from record due to error return prepend0x(createHash('keccak256').update(transactionBuffer).digest('hex')); @@ -1538,21 +1549,21 @@ export class EthImpl implements Eth { /** * Submits a transaction to the network for execution. * - * @param transaction - * @param requestId + * @param {string} transaction The raw transaction to submit + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async sendRawTransaction(transaction: string, requestId: string): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); + async sendRawTransaction(transaction: string, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; if (transaction?.length >= constants.FUNCTION_SELECTOR_CHAR_LENGTH) this.ethExecutionsCounter .labels(EthImpl.ethSendRawTransaction, transaction.substring(0, constants.FUNCTION_SELECTOR_CHAR_LENGTH)) .inc(); const networkGasPriceInWeiBars = Utils.addPercentageBufferToGasPrice( - await this.getFeeWeibars(EthImpl.ethGasPrice, requestIdPrefix), + await this.getFeeWeibars(EthImpl.ethGasPrice, requestDetails), ); - const parsedTx = await this.parseRawTxAndPrecheck(transaction, networkGasPriceInWeiBars, requestIdPrefix); + const parsedTx = await this.parseRawTxAndPrecheck(transaction, requestDetails, networkGasPriceInWeiBars); const originalCallerAddress = parsedTx.from?.toString() || ''; const transactionBuffer = Buffer.from(EthImpl.prune0x(transaction), 'hex'); let fileId: FileId | null = null; @@ -1563,10 +1574,10 @@ export class EthImpl implements Eth { .submitEthereumTransaction( transactionBuffer, EthImpl.ethSendRawTransaction, + requestDetails, originalCallerAddress, networkGasPriceInWeiBars, - await this.getCurrentNetworkExchangeRateInCents(requestIdPrefix), - requestIdPrefix, + await this.getCurrentNetworkExchangeRateInCents(requestDetails), ); txSubmitted = true; @@ -1583,9 +1594,9 @@ export class EthImpl implements Eth { const contractResult = await this.mirrorNodeClient.repeatedRequest( this.mirrorNodeClient.getContractResult.name, - [formattedId], + [formattedId, requestDetails], this.mirrorNodeClient.getMirrorNodeRequestRetryCount(), - requestIdPrefix, + requestDetails, ); if (!contractResult) { @@ -1608,7 +1619,7 @@ export class EthImpl implements Eth { transactionBuffer, txSubmitted, parsedTx, - requestIdPrefix, + requestDetails, ); } finally { /** @@ -1618,7 +1629,8 @@ export class EthImpl implements Eth { if (fileId) { this.hapiService .getSDKClient() - .deleteFile(fileId, requestIdPrefix, EthImpl.ethSendRawTransaction, fileId.toString(), originalCallerAddress); + .deleteFile(fileId, requestDetails, EthImpl.ethSendRawTransaction, fileId.toString(), originalCallerAddress) + .then(); } } } @@ -1626,15 +1638,16 @@ export class EthImpl implements Eth { /** * Execute a free contract call query. * - * @param call {IContractCallRequest} The contract call request data. - * @param blockParam either a string (blockNumber or blockTag) or an object (blockHash or blockNumber) - * @param requestIdPrefix optional request ID prefix for logging. + * @param {IContractCallRequest} call The contract call request data. + * @param {string | object | null} blockParam either a string (blockNumber or blockTag) or an object (blockHash or blockNumber) + * @param {RequestDetails} requestDetails The request details for logging and tracking */ async call( call: IContractCallRequest, blockParam: string | object | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; const callData = call.data ? call.data : call.input; // log request this.logger.info( @@ -1650,13 +1663,13 @@ export class EthImpl implements Eth { .inc(); } - const blockNumberOrTag = await this.extractBlockNumberOrTag(blockParam, requestIdPrefix); + const blockNumberOrTag = await this.extractBlockNumberOrTag(blockParam, requestDetails); await this.performCallChecks(call); // Get a reasonable value for "gas" if it is not specified. - const gas = this.getCappedBlockGasLimit(call.gas?.toString(), requestIdPrefix); + const gas = this.getCappedBlockGasLimit(call.gas?.toString(), requestDetails); - await this.contractCallFormat(call, requestIdPrefix); + await this.contractCallFormat(call, requestDetails); const selector = getFunctionSelector(call.data!); @@ -1673,11 +1686,11 @@ export class EthImpl implements Eth { let result: string | JsonRpcError = ''; try { if (shouldForceToConsensus || shouldDefaultToConsensus) { - result = await this.callConsensusNode(call, gas, requestIdPrefix); + result = await this.callConsensusNode(call, gas, requestDetails); } else { //temporary workaround until precompiles are implemented in Mirror node evm module // Execute the call and get the response - result = await this.callMirrorNode(call, gas, call.value, blockNumberOrTag, requestIdPrefix); + result = await this.callMirrorNode(call, gas, call.value, blockNumberOrTag, requestDetails); } this.logger.debug(`${requestIdPrefix} eth_call response: ${JSON.stringify(result)}`); @@ -1694,10 +1707,12 @@ export class EthImpl implements Eth { /** * Gets transactions by block hash or block number and index with resolved EVM addresses - * @param blockParam - * @param transactionIndex - * @param requestIdPrefix - * @returns Promise + * @param {object} blockParam The block parameter + * @param {string} blockParam.title Possible values are 'blockHash' and 'blockNumber' + * @param {string | number} blockParam.value The block hash or block number + * @param {string} transactionIndex + * @param {RequestDetails} requestDetails The request details for logging and tracking + * @returns {Promise} The transaction or null if not found */ private async getTransactionByBlockHashOrBlockNumAndIndex( blockParam: { @@ -1705,21 +1720,21 @@ export class EthImpl implements Eth { value: string | number; }, transactionIndex: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { const contractResults = await this.mirrorNodeClient.getContractResults( + requestDetails, { [blockParam.title]: blockParam.value, transactionIndex: Number(transactionIndex), }, undefined, - requestIdPrefix, ); if (!contractResults[0]) return null; - const resolvedToAddress = await this.resolveEvmAddress(contractResults[0].to, requestIdPrefix); - const resolvedFromAddress = await this.resolveEvmAddress(contractResults[0].from, requestIdPrefix, [ + const resolvedToAddress = await this.resolveEvmAddress(contractResults[0].to, requestDetails); + const resolvedFromAddress = await this.resolveEvmAddress(contractResults[0].from, requestDetails, [ constants.TYPE_ACCOUNT, ]); @@ -1729,7 +1744,7 @@ export class EthImpl implements Eth { // according to EIP-1898 (https://eips.ethereum.org/EIPS/eip-1898) block param can either be a string (blockNumber or Block Tag) or an object (blockHash or blockNumber) private async extractBlockNumberOrTag( blockParam: string | object | null, - requestIdPrefix: string | undefined, + requestDetails: RequestDetails, ): Promise { if (!blockParam) { return null; @@ -1743,7 +1758,7 @@ export class EthImpl implements Eth { } if (blockParam['blockHash'] != null) { - return await this.getBlockNumberFromHash(blockParam['blockHash'], requestIdPrefix); + return await this.getBlockNumberFromHash(blockParam['blockHash'], requestDetails); } // if is an object but doesn't have blockNumber or blockHash, then it's an invalid blockParam @@ -1751,10 +1766,10 @@ export class EthImpl implements Eth { } // if blockParam is a string, could be a blockNumber or blockTag or blockHash - if (typeof blockParam === 'string' && blockParam.length > 0) { + if (blockParam.length > 0) { // if string is a blockHash, we return its corresponding blockNumber if (EthImpl.isBlockHash(blockParam)) { - return await this.getBlockNumberFromHash(blockParam, requestIdPrefix); + return await this.getBlockNumberFromHash(blockParam, requestDetails); } else { return blockParam; } @@ -1763,8 +1778,8 @@ export class EthImpl implements Eth { return null; } - private async getBlockNumberFromHash(blockHash: string, requestIdPrefix: string | undefined): Promise { - const block = await this.getBlockByHash(blockHash, false, requestIdPrefix); + private async getBlockNumberFromHash(blockHash: string, requestDetails: RequestDetails): Promise { + const block = await this.getBlockByHash(blockHash, false, requestDetails); if (block != null) { return block.number; } else { @@ -1777,8 +1792,9 @@ export class EthImpl implements Eth { gas: number | null, value: number | string | null | undefined, block: string | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; let callData: IContractCallRequest = {}; try { this.logger.debug( @@ -1797,7 +1813,7 @@ export class EthImpl implements Eth { ...(block !== null ? { block } : {}), }; - const contractCallResponse = await this.mirrorNodeClient.postContractCall(callData, requestIdPrefix); + const contractCallResponse = await this.mirrorNodeClient.postContractCall(callData, requestDetails); return contractCallResponse?.result ? prepend0x(contractCallResponse.result) : EthImpl.emptyHex; } catch (e: any) { if (e instanceof JsonRpcError) { @@ -1831,7 +1847,7 @@ export class EthImpl implements Eth { callData, )} with error: "${e.message}"`, ); - return await this.callConsensusNode(call, gas, requestIdPrefix); + return await this.callConsensusNode(call, gas, requestDetails); } } @@ -1844,11 +1860,16 @@ export class EthImpl implements Eth { /** * Execute a contract call query to the consensus node * - * @param call - * @param gas - * @param requestIdPrefix + * @param call The contract call request data + * @param {number | null} gas The gas to pass for the call + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async callConsensusNode(call: any, gas: number | null, requestIdPrefix?: string): Promise { + async callConsensusNode( + call: any, + gas: number | null, + requestDetails: RequestDetails, + ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; // Execute the call and get the response if (!gas) { gas = Number.parseInt(this.defaultGas); @@ -1881,7 +1902,7 @@ export class EthImpl implements Eth { } const cacheKey = `${constants.CACHE_KEY.ETH_CALL}:${call.from || ''}.${call.to}.${data}`; - const cachedResponse = await this.cacheService.getAsync(cacheKey, EthImpl.ethCall, requestIdPrefix); + const cachedResponse = await this.cacheService.getAsync(cacheKey, EthImpl.ethCall, requestDetails); if (cachedResponse != undefined) { this.logger.debug(`${requestIdPrefix} eth_call returned cached response: ${cachedResponse}`); @@ -1890,7 +1911,7 @@ export class EthImpl implements Eth { const contractCallResponse = await this.hapiService .getSDKClient() - .submitContractCallQueryWithRetry(call.to, call.data, gas, call.from, EthImpl.ethCall, requestIdPrefix); + .submitContractCallQueryWithRetry(call.to, call.data, gas, call.from, EthImpl.ethCall, requestDetails); if (contractCallResponse) { const formattedCallReponse = prepend0x(Buffer.from(contractCallResponse.asBytes()).toString('hex')); @@ -1898,8 +1919,8 @@ export class EthImpl implements Eth { cacheKey, formattedCallReponse, EthImpl.ethCall, + requestDetails, this.ethCallCacheTtl, - requestIdPrefix, ); return formattedCallReponse; } @@ -1934,16 +1955,16 @@ export class EthImpl implements Eth { async resolveEvmAddress( address: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, searchableTypes = [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT], ): Promise { if (!address) return address; const entity = await this.mirrorNodeClient.resolveEntityType( address, - searchableTypes, EthImpl.ethGetCode, - requestIdPrefix, + requestDetails, + searchableTypes, 0, ); let resolvedAddress = address; @@ -1962,12 +1983,13 @@ export class EthImpl implements Eth { * Gets a transaction by the provided hash * * @param hash - * @param requestIdPrefix + * @param requestDetails */ - async getTransactionByHash(hash: string, requestIdPrefix?: string): Promise { + async getTransactionByHash(hash: string, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getTransactionByHash(hash=${hash})`, hash); - const contractResult = await this.mirrorNodeClient.getContractResultWithRetry(hash, requestIdPrefix); + const contractResult = await this.mirrorNodeClient.getContractResultWithRetry(hash, requestDetails); if (contractResult === null || contractResult.hash === undefined) { // handle synthetic transactions const syntheticLogs = await this.common.getLogsWithParams( @@ -1975,7 +1997,7 @@ export class EthImpl implements Eth { { 'transaction.hash': hash, }, - requestIdPrefix, + requestDetails, ); // no tx found @@ -1993,8 +2015,8 @@ export class EthImpl implements Eth { ); } - const fromAddress = await this.resolveEvmAddress(contractResult.from, requestIdPrefix, [constants.TYPE_ACCOUNT]); - const toAddress = await this.resolveEvmAddress(contractResult.to, requestIdPrefix); + const fromAddress = await this.resolveEvmAddress(contractResult.from, requestDetails, [constants.TYPE_ACCOUNT]); + const toAddress = await this.resolveEvmAddress(contractResult.to, requestDetails); contractResult.chain_id = contractResult.chain_id || this.chain; return formatContractResult({ @@ -2007,18 +2029,15 @@ export class EthImpl implements Eth { /** * Gets a receipt for a transaction that has already executed. * - * @param hash - * @param requestIdPrefix + * @param {string} hash The transaction hash + * @param {RequestDetails} requestDetails The request details for logging and tracking */ - async getTransactionReceipt(hash: string, requestIdPrefix?: string): Promise { + async getTransactionReceipt(hash: string, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} getTransactionReceipt(${hash})`); const cacheKey = `${constants.CACHE_KEY.ETH_GET_TRANSACTION_RECEIPT}_${hash}`; - const cachedResponse = await this.cacheService.getAsync( - cacheKey, - EthImpl.ethGetTransactionReceipt, - requestIdPrefix, - ); + const cachedResponse = await this.cacheService.getAsync(cacheKey, EthImpl.ethGetTransactionReceipt, requestDetails); if (cachedResponse) { this.logger.debug( `${requestIdPrefix} getTransactionReceipt returned cached response: ${JSON.stringify(cachedResponse)}`, @@ -2026,7 +2045,7 @@ export class EthImpl implements Eth { return cachedResponse; } - const receiptResponse = await this.mirrorNodeClient.getContractResultWithRetry(hash, requestIdPrefix); + const receiptResponse = await this.mirrorNodeClient.getContractResultWithRetry(hash, requestDetails); if (receiptResponse === null || receiptResponse.hash === undefined) { // handle synthetic transactions const syntheticLogs = await this.common.getLogsWithParams( @@ -2034,7 +2053,7 @@ export class EthImpl implements Eth { { 'transaction.hash': hash, }, - requestIdPrefix, + requestDetails, ); // no tx found @@ -2043,7 +2062,7 @@ export class EthImpl implements Eth { return null; } - const gasPriceForTimestamp = await this.getCurrentGasPriceForBlock(syntheticLogs[0].blockHash); + const gasPriceForTimestamp = await this.getCurrentGasPriceForBlock(syntheticLogs[0].blockHash, requestDetails); const receipt: ITransactionReceipt = { blockHash: syntheticLogs[0].blockHash, blockNumber: syntheticLogs[0].blockNumber, @@ -2068,12 +2087,12 @@ export class EthImpl implements Eth { cacheKey, receipt, EthImpl.ethGetTransactionReceipt, + requestDetails, constants.CACHE_TTL.ONE_DAY, - requestIdPrefix, ); return receipt; } else { - const effectiveGas = await this.getCurrentGasPriceForBlock(receiptResponse.blockHash); + const effectiveGas = await this.getCurrentGasPriceForBlock(receiptResponse.blockHash, requestDetails); // support stricter go-eth client which requires the transaction hash property on logs const logs = receiptResponse.logs.map((log) => { return new Log({ @@ -2092,8 +2111,8 @@ export class EthImpl implements Eth { const receipt: ITransactionReceipt = { blockHash: toHash32(receiptResponse.block_hash), blockNumber: numberTo0x(receiptResponse.block_number), - from: await this.resolveEvmAddress(receiptResponse.from, requestIdPrefix), - to: await this.resolveEvmAddress(receiptResponse.to, requestIdPrefix), + from: await this.resolveEvmAddress(receiptResponse.from, requestDetails), + to: await this.resolveEvmAddress(receiptResponse.to, requestDetails), cumulativeGasUsed: numberTo0x(receiptResponse.block_gas_used), gasUsed: nanOrNumberTo0x(receiptResponse.gas_used), contractAddress: receiptResponse.address, @@ -2119,20 +2138,20 @@ export class EthImpl implements Eth { cacheKey, receipt, EthImpl.ethGetTransactionReceipt, + requestDetails, constants.CACHE_TTL.ONE_DAY, - requestIdPrefix, ); return receipt; } } - private async getCurrentGasPriceForBlock(blockHash: string, requestIdPrefix?: string): Promise { - const block = await this.getBlockByHash(blockHash, false); + private async getCurrentGasPriceForBlock(blockHash: string, requestDetails: RequestDetails): Promise { + const block = await this.getBlockByHash(blockHash, false, requestDetails); const timestampDecimal = parseInt(block ? block.timestamp : '0', 16); const timestampDecimalString = timestampDecimal > 0 ? timestampDecimal.toString() : ''; const gasPriceForTimestamp = await this.getFeeWeibars( EthImpl.ethGetTransactionReceipt, - requestIdPrefix, + requestDetails, timestampDecimalString, ); @@ -2177,12 +2196,12 @@ export class EthImpl implements Eth { * most recent block, 'earliest' is 0, numbers become numbers. * * @param tag null, a number, or 'latest', 'pending', or 'earliest' - * @param requestIdPrefix + * @param requestDetails * @private */ - private async translateBlockTag(tag: string | null, requestIdPrefix?: string): Promise { + private async translateBlockTag(tag: string | null, requestDetails: RequestDetails): Promise { if (this.common.blockTagIsLatestOrPending(tag)) { - return Number(await this.blockNumber(requestIdPrefix)); + return Number(await this.blockNumber(requestDetails)); } else if (tag === EthImpl.blockEarliest) { return 0; } else { @@ -2190,7 +2209,7 @@ export class EthImpl implements Eth { } } - private getCappedBlockGasLimit(gasString: string | undefined, requestIdPrefix?: string): number | null { + private getCappedBlockGasLimit(gasString: string | undefined, requestDetails: RequestDetails): number | null { if (!gasString) { // Return null and don't include in the mirror node call, as mirror is doing this estimation on the go. return null; @@ -2201,7 +2220,7 @@ export class EthImpl implements Eth { const gas = Number.parseInt(gasString); if (gas > constants.MAX_GAS_PER_SEC) { this.logger.trace( - `${requestIdPrefix} eth_call gas amount (${gas}) exceeds network limit, capping gas to ${constants.MAX_GAS_PER_SEC}`, + `${requestDetails.formattedRequestId} eth_call gas amount (${gas}) exceeds network limit, capping gas to ${constants.MAX_GAS_PER_SEC}`, ); return constants.MAX_GAS_PER_SEC; } @@ -2213,7 +2232,7 @@ export class EthImpl implements Eth { showDetails: boolean, logs: Log[], transactionsArray: Array, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Array { let filteredLogs: Log[]; if (showDetails) { @@ -2231,7 +2250,9 @@ export class EthImpl implements Eth { }); } - this.logger.trace(`${requestIdPrefix} Synthetic transaction hashes will be populated in the block response`); + this.logger.trace( + `${requestDetails.formattedRequestId} Synthetic transaction hashes will be populated in the block response`, + ); return transactionsArray; } @@ -2242,30 +2263,30 @@ export class EthImpl implements Eth { * Then using the block timerange get all contract results to get transaction details. * If showDetails is set to true subsequently call mirror node for additional transaction details * - * @param blockHashOrNumber - * @param showDetails - * @param requestIdPrefix + * @param {string} blockHashOrNumber The block hash or block number + * @param {boolean} showDetails Whether to show transaction details + * @param {RequestDetails} requestDetails The request details for logging and tracking */ private async getBlock( blockHashOrNumber: string, showDetails: boolean, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { - const blockResponse = await this.common.getHistoricalBlockResponse(blockHashOrNumber, true, requestIdPrefix); + const blockResponse = await this.common.getHistoricalBlockResponse(requestDetails, blockHashOrNumber, true); if (blockResponse == null) return null; const timestampRange = blockResponse.timestamp; const timestampRangeParams = [`gte:${timestampRange.from}`, `lte:${timestampRange.to}`]; const contractResults = await this.mirrorNodeClient.getContractResults( + requestDetails, { timestamp: timestampRangeParams }, undefined, - requestIdPrefix, ); const gasUsed = blockResponse.gas_used; const params = { timestamp: timestampRangeParams }; // get contract results logs using block timestamp range - const logs = await this.common.getLogsWithParams(null, params, requestIdPrefix); + const logs = await this.common.getLogsWithParams(null, params, requestDetails); if (contractResults == null && logs.length == 0) { // contract result not found @@ -2281,16 +2302,14 @@ export class EthImpl implements Eth { // prepare transactionArray let transactionArray: any[] = []; for (const contractResult of contractResults) { - contractResult.from = await this.resolveEvmAddress(contractResult.from, requestIdPrefix, [ - constants.TYPE_ACCOUNT, - ]); - contractResult.to = await this.resolveEvmAddress(contractResult.to, requestIdPrefix); + contractResult.from = await this.resolveEvmAddress(contractResult.from, requestDetails, [constants.TYPE_ACCOUNT]); + contractResult.to = await this.resolveEvmAddress(contractResult.to, requestDetails); contractResult.chain_id = contractResult.chain_id || this.chain; transactionArray.push(showDetails ? formatContractResult(contractResult) : contractResult.hash); } - transactionArray = this.populateSyntheticTransactions(showDetails, logs, transactionArray, requestIdPrefix); + transactionArray = this.populateSyntheticTransactions(showDetails, logs, transactionArray, requestDetails); transactionArray = showDetails ? _.uniqBy(transactionArray, 'hash') : _.uniq(transactionArray); const formattedReceipts: IReceiptRootHash[] = ReceiptsRootUtils.buildReceiptRootHashes( @@ -2301,7 +2320,7 @@ export class EthImpl implements Eth { const blockHash = toHash32(blockResponse.hash); return new Block({ - baseFeePerGas: await this.gasPrice(requestIdPrefix), + baseFeePerGas: await this.gasPrice(requestDetails), difficulty: EthImpl.zeroHex, extraData: EthImpl.emptyHex, gasLimit: numberTo0x(constants.BLOCK_GAS_LIMIT), @@ -2358,8 +2377,8 @@ export class EthImpl implements Eth { return numberTo0x(block.count); } - private async getAccountLatestEthereumNonce(address: string, requestId?: string): Promise { - const accountData = await this.mirrorNodeClient.getAccount(address, requestId); + private async getAccountLatestEthereumNonce(address: string, requestDetails: RequestDetails): Promise { + const accountData = await this.mirrorNodeClient.getAccount(address, requestDetails); if (accountData) { // with HIP 729 ethereum_nonce should always be 0+ and null. Historical contracts may have a null value as the nonce was not tracked, return default EVM compliant 0x1 in this case return accountData.ethereum_nonce !== null ? numberTo0x(accountData.ethereum_nonce) : EthImpl.oneHex; @@ -2372,18 +2391,19 @@ export class EthImpl implements Eth { * Returns the number of transactions sent from an address by searching for the ethereum transaction involving the address * Remove when https://github.com/hashgraph/hedera-mirror-node/issues/5862 is implemented * - * @param address string - * @param blockNumOrHash - * @param requestIdPrefix - * @returns string + * @param {string} address The account address + * @param {string | number} blockNumOrHash The block number or hash + * @param {RequestDetails} requestDetails The request details for logging and tracking + * @returns {Promise} The number of transactions sent from the address */ private async getAcccountNonceFromContractResult( address: string, - blockNumOrHash: any, - requestIdPrefix: string | undefined, + blockNumOrHash: string | number, + requestDetails: RequestDetails, ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; // get block timestamp for blockNum - const block = await this.mirrorNodeClient.getBlock(blockNumOrHash, requestIdPrefix); // consider caching error responses + const block = await this.mirrorNodeClient.getBlock(blockNumOrHash, requestDetails); // consider caching error responses if (block == null) { throw predefined.UNKNOWN_BLOCK(); } @@ -2392,8 +2412,8 @@ export class EthImpl implements Eth { const ethereumTransactions = await this.mirrorNodeClient.getAccountLatestEthereumTransactionsByTimestamp( address, block.timestamp.to, + requestDetails, 2, - requestIdPrefix, ); if (ethereumTransactions == null || ethereumTransactions.transactions.length === 0) { return EthImpl.zeroHex; @@ -2408,7 +2428,7 @@ export class EthImpl implements Eth { // get the transaction result for the latest transaction const transactionResult = await this.mirrorNodeClient.getContractResult( ethereumTransactions.transactions[0].transaction_id, - requestIdPrefix, + requestDetails, ); if (transactionResult == null) { throw predefined.RESOURCE_NOT_FOUND( @@ -2416,20 +2436,20 @@ export class EthImpl implements Eth { ); } - const accountResult = await this.mirrorNodeClient.getAccount(transactionResult.from); + const accountResult = await this.mirrorNodeClient.getAccount(transactionResult.from, requestDetails); if (accountResult.evm_address !== address.toLowerCase()) { this.logger.warn( `${requestIdPrefix} eth_transactionCount for a historical block was requested where address: ${address} was not sender: ${transactionResult.address}, returning latest value as best effort.`, ); - return await this.getAccountLatestEthereumNonce(address, requestIdPrefix); + return await this.getAccountLatestEthereumNonce(address, requestDetails); } return numberTo0x(transactionResult.nonce + 1); // nonce is 0 indexed } - private async getAccountNonceForEarliestBlock(requestIdPrefix?: string): Promise { - const block = await this.mirrorNodeClient.getEarliestBlock(requestIdPrefix); + private async getAccountNonceForEarliestBlock(requestDetails: RequestDetails): Promise { + const block = await this.mirrorNodeClient.getEarliestBlock(requestDetails); if (block == null) { throw predefined.INTERNAL_ERROR('No network blocks found'); } @@ -2446,33 +2466,33 @@ export class EthImpl implements Eth { private async getAccountNonceForHistoricBlock( address: string, blockNumOrHash: number | string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { let getBlock; - const isParamBlockNum = typeof blockNumOrHash === 'number' ? true : false; + const isParamBlockNum = typeof blockNumOrHash === 'number'; if (isParamBlockNum && (blockNumOrHash as number) < 0) { throw predefined.UNKNOWN_BLOCK(); } if (!isParamBlockNum) { - getBlock = await this.mirrorNodeClient.getBlock(blockNumOrHash); + getBlock = await this.mirrorNodeClient.getBlock(blockNumOrHash, requestDetails); } const blockNum = isParamBlockNum ? blockNumOrHash : getBlock.number; // check if on latest block, if so get latest ethereumNonce from mirror node account API - const blockResponse = await this.mirrorNodeClient.getLatestBlock(requestIdPrefix); // consider caching error responses + const blockResponse = await this.mirrorNodeClient.getLatestBlock(requestDetails); // consider caching error responses if (blockResponse == null || blockResponse.blocks.length === 0) { throw predefined.UNKNOWN_BLOCK(); } if (blockResponse.blocks[0].number - blockNum <= this.maxBlockRange) { - return this.getAccountLatestEthereumNonce(address, requestIdPrefix); + return this.getAccountLatestEthereumNonce(address, requestDetails); } // if valid block number, get block timestamp - return await this.getAcccountNonceFromContractResult(address, blockNum, requestIdPrefix); + return await this.getAcccountNonceFromContractResult(address, blockNum, requestDetails); } async getLogs( @@ -2481,13 +2501,13 @@ export class EthImpl implements Eth { toBlock: string | 'latest', address: string | string[] | null, topics: any[] | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { - return this.common.getLogs(blockHash, fromBlock, toBlock, address, topics, requestIdPrefix); + return this.common.getLogs(blockHash, fromBlock, toBlock, address, topics, requestDetails); } - async maxPriorityFeePerGas(requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} maxPriorityFeePerGas()`); + async maxPriorityFeePerGas(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} maxPriorityFeePerGas()`); return EthImpl.zeroHex; } @@ -2524,17 +2544,16 @@ export class EthImpl implements Eth { * @param {string} requestId - The unique identifier for the request. * @returns {Promise} - A promise that resolves to the current exchange rate in cents. */ - private async getCurrentNetworkExchangeRateInCents(requestId: string): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); + private async getCurrentNetworkExchangeRateInCents(requestDetails: RequestDetails): Promise { const cacheKey = constants.CACHE_KEY.CURRENT_NETWORK_EXCHANGE_RATE; const callingMethod = this.getCurrentNetworkExchangeRateInCents.name; const cacheTTL = 15 * 60 * 1000; // 15 minutes - let currentNetworkExchangeRate = await this.cacheService.getAsync(cacheKey, callingMethod, requestIdPrefix); + let currentNetworkExchangeRate = await this.cacheService.getAsync(cacheKey, callingMethod, requestDetails); if (!currentNetworkExchangeRate) { - currentNetworkExchangeRate = (await this.mirrorNodeClient.getNetworkExchangeRate(requestId)).current_rate; - await this.cacheService.set(cacheKey, currentNetworkExchangeRate, callingMethod, cacheTTL, requestIdPrefix); + currentNetworkExchangeRate = (await this.mirrorNodeClient.getNetworkExchangeRate(requestDetails)).current_rate; + await this.cacheService.set(cacheKey, currentNetworkExchangeRate, callingMethod, requestDetails, cacheTTL); } const exchangeRateInCents = currentNetworkExchangeRate.cent_equivalent / currentNetworkExchangeRate.hbar_equivalent; diff --git a/packages/relay/src/lib/hbarlimiter/index.ts b/packages/relay/src/lib/hbarlimiter/index.ts index 818d442e7b..2b25a657f5 100644 --- a/packages/relay/src/lib/hbarlimiter/index.ts +++ b/packages/relay/src/lib/hbarlimiter/index.ts @@ -20,9 +20,9 @@ import { Logger } from 'pino'; import constants from '../constants'; -import { predefined } from '../errors/JsonRpcError'; import { Registry, Counter, Gauge } from 'prom-client'; import { formatRequestIdMessage } from '../../formatters'; +import { RequestDetails } from '../types'; export default class HbarLimit { private enabled: boolean = false; @@ -79,7 +79,7 @@ export default class HbarLimit { * @param {string} mode - The mode of the transaction or request. * @param {string} methodName - The name of the method being invoked. * @param {string} originalCallerAddress - The address of the original caller making the request. - * @param {string} [requestId] - An optional unique request ID for tracking the request. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {boolean} - Returns `true` if the rate limit should be enforced, otherwise `false`. */ shouldLimit( @@ -87,13 +87,13 @@ export default class HbarLimit { mode: string, methodName: string, originalCallerAddress: string, - requestId?: string, + requestDetails: RequestDetails, ): boolean { if (!this.enabled) { return false; } - const requestIdPrefix = formatRequestIdMessage(requestId); + const requestIdPrefix = requestDetails.formattedRequestId; // check if the caller is a whitelisted caller if (this.isAccountWhiteListed(originalCallerAddress)) { @@ -104,7 +104,7 @@ export default class HbarLimit { } if (this.shouldResetLimiter(currentDateNow)) { - this.resetLimiter(currentDateNow, requestIdPrefix); + this.resetLimiter(currentDateNow, requestDetails); } if (this.remainingBudget <= 0) { @@ -139,13 +139,11 @@ export default class HbarLimit { callDataSize: number, fileChunkSize: number, currentNetworkExchangeRateInCents: number, - requestId: string, + requestDetails: RequestDetails, ): boolean { - const requestIdPrefix = formatRequestIdMessage(requestId); - if (this.isAccountWhiteListed(originalCallerAddress)) { this.logger.trace( - `${requestIdPrefix} Request bypasses the preemptive limit check - the caller is a whitelisted account: originalCallerAddress=${originalCallerAddress}`, + `${requestDetails.formattedRequestId} Request bypasses the preemptive limit check - the caller is a whitelisted account: originalCallerAddress=${originalCallerAddress}`, ); return false; } @@ -158,13 +156,13 @@ export default class HbarLimit { if (this.remainingBudget - estimatedTxFee < 0) { this.logger.warn( - `${requestIdPrefix} Request fails the preemptive limit check - the remaining HBAR budget was not enough to accommodate the estimated transaction fee: remainingBudget=${this.remainingBudget}, total=${this.total}, resetTimestamp=${this.reset}, callDataSize=${callDataSize}, estimatedTxFee=${estimatedTxFee}, exchangeRateInCents=${currentNetworkExchangeRateInCents}`, + `${requestDetails.formattedRequestId} Request fails the preemptive limit check - the remaining HBAR budget was not enough to accommodate the estimated transaction fee: remainingBudget=${this.remainingBudget}, total=${this.total}, resetTimestamp=${this.reset}, callDataSize=${callDataSize}, estimatedTxFee=${estimatedTxFee}, exchangeRateInCents=${currentNetworkExchangeRateInCents}`, ); return true; } this.logger.trace( - `${requestIdPrefix} Request passes the preemptive limit check - the remaining HBAR budget is enough to accommodate the estimated transaction fee: remainingBudget=${this.remainingBudget}, total=${this.total}, resetTimestamp=${this.reset}, callDataSize=${callDataSize}, estimatedTxFee=${estimatedTxFee}, exchangeRateInCents=${currentNetworkExchangeRateInCents}`, + `${requestDetails.formattedRequestId} Request passes the preemptive limit check - the remaining HBAR budget is enough to accommodate the estimated transaction fee: remainingBudget=${this.remainingBudget}, total=${this.total}, resetTimestamp=${this.reset}, callDataSize=${callDataSize}, estimatedTxFee=${estimatedTxFee}, exchangeRateInCents=${currentNetworkExchangeRateInCents}`, ); return false; } @@ -172,21 +170,19 @@ export default class HbarLimit { /** * Add expense to the remaining budget. */ - addExpense(cost: number, currentDateNow: number, requestId?: string) { - const requestIdPrefix = formatRequestIdMessage(requestId); - + addExpense(cost: number, currentDateNow: number, requestDetails: RequestDetails) { if (!this.enabled) { return; } if (this.shouldResetLimiter(currentDateNow)) { - this.resetLimiter(currentDateNow, requestIdPrefix); + this.resetLimiter(currentDateNow, requestDetails); } this.remainingBudget -= cost; this.hbarLimitRemainingGauge.set(this.remainingBudget); this.logger.trace( - `${requestIdPrefix} HBAR rate limit expense update: cost=${cost}, remainingBudget=${this.remainingBudget}, resetTimestamp=${this.reset}`, + `${requestDetails.formattedRequestId} HBAR rate limit expense update: cost=${cost}, remainingBudget=${this.remainingBudget}, resetTimestamp=${this.reset}`, ); } @@ -264,17 +260,17 @@ export default class HbarLimit { * Decides whether it should reset budget and timer. */ private shouldResetLimiter(currentDateNow: number): boolean { - return this.reset < currentDateNow ? true : false; + return this.reset < currentDateNow; } /** * Reset budget to the total allowed and reset timer to current time plus duration. */ - private resetLimiter(currentDateNow: number, requestIdPrefix: string) { + private resetLimiter(currentDateNow: number, requestDetails: RequestDetails) { this.reset = currentDateNow + this.duration; this.remainingBudget = this.total; this.logger.trace( - `${requestIdPrefix} HBAR Rate Limit reset: remainingBudget= ${this.remainingBudget}, newResetTimestamp= ${this.reset}`, + `${requestDetails.formattedRequestId} HBAR Rate Limit reset: remainingBudget= ${this.remainingBudget}, newResetTimestamp= ${this.reset}`, ); } } diff --git a/packages/relay/src/lib/poller.ts b/packages/relay/src/lib/poller.ts index 32cc23348f..1c5e621a28 100644 --- a/packages/relay/src/lib/poller.ts +++ b/packages/relay/src/lib/poller.ts @@ -21,6 +21,8 @@ import { Eth } from '../index'; import { Logger } from 'pino'; import { Registry, Gauge } from 'prom-client'; +import { RequestDetails } from './types'; +import { Utils } from '../utils'; export interface Poll { tag: string; @@ -31,15 +33,15 @@ export interface Poll { const LOGGER_PREFIX = 'Poller:'; export class Poller { - private eth: Eth; - private logger: Logger; + private readonly eth: Eth; + private readonly logger: Logger; private polls: Poll[]; private interval?: NodeJS.Timer; private latestBlock?: string; - private pollingInterval: number; - private newHeadsEnabled: boolean; - private activePollsGauge: Gauge; - private activeNewHeadsPollsGauge: Gauge; + private readonly pollingInterval: number; + private readonly newHeadsEnabled: boolean; + private readonly activePollsGauge: Gauge; + private readonly activeNewHeadsPollsGauge: Gauge; private NEW_HEADS_EVENT = 'newHeads'; @@ -83,11 +85,16 @@ export class Poller { 'latest', filters?.address || null, filters?.topics || null, + new RequestDetails({ requestId: Utils.generateRequestId(), ipAddress: '' }), ); poll.lastPolled = this.latestBlock; } else if (event === this.NEW_HEADS_EVENT && this.newHeadsEnabled) { - data = await this.eth.getBlockByNumber('latest', filters?.includeTransactions ?? false); + data = await this.eth.getBlockByNumber( + 'latest', + filters?.includeTransactions ?? false, + new RequestDetails({ requestId: Utils.generateRequestId(), ipAddress: '' }), + ); data.jsonrpc = '2.0'; poll.lastPolled = this.latestBlock; } else { @@ -114,7 +121,9 @@ export class Poller { start() { this.logger.info(`${LOGGER_PREFIX} Starting polling with interval=${this.pollingInterval}`); this.interval = setInterval(async () => { - this.latestBlock = await this.eth.blockNumber(); + this.latestBlock = await this.eth.blockNumber( + new RequestDetails({ requestId: Utils.generateRequestId(), ipAddress: '' }), + ); this.poll(); }, this.pollingInterval); } diff --git a/packages/relay/src/lib/precheck.ts b/packages/relay/src/lib/precheck.ts index 1c09d1be85..f44c1d3d51 100644 --- a/packages/relay/src/lib/precheck.ts +++ b/packages/relay/src/lib/precheck.ts @@ -24,7 +24,8 @@ import { EthImpl } from './eth'; import { Logger } from 'pino'; import constants from './constants'; import { ethers, Transaction } from 'ethers'; -import { formatRequestIdMessage, prepend0x } from '../formatters'; +import { prepend0x } from '../formatters'; +import { RequestDetails } from './types'; /** * Precheck class for handling various prechecks before sending a raw transaction. @@ -69,36 +70,34 @@ export class Precheck { * Sends a raw transaction after performing various prechecks. * @param {ethers.Transaction} parsedTx - The parsed transaction. * @param {number} networkGasPriceInWeiBars - The predefined gas price of the network in weibar. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ async sendRawTransactionCheck( parsedTx: ethers.Transaction, networkGasPriceInWeiBars: number, - requestId?: string, + requestDetails: RequestDetails, ): Promise { - this.transactionType(parsedTx, requestId); - this.gasLimit(parsedTx, requestId); - const mirrorAccountInfo = await this.verifyAccount(parsedTx, requestId); - this.nonce(parsedTx, mirrorAccountInfo.ethereum_nonce, requestId); - this.chainId(parsedTx, requestId); + this.transactionType(parsedTx, requestDetails); + this.gasLimit(parsedTx, requestDetails); + const mirrorAccountInfo = await this.verifyAccount(parsedTx, requestDetails); + this.nonce(parsedTx, mirrorAccountInfo.ethereum_nonce, requestDetails); + this.chainId(parsedTx, requestDetails); this.value(parsedTx); - this.gasPrice(parsedTx, networkGasPriceInWeiBars, requestId); - this.balance(parsedTx, mirrorAccountInfo, requestId); + this.gasPrice(parsedTx, networkGasPriceInWeiBars, requestDetails); + this.balance(parsedTx, mirrorAccountInfo, requestDetails); } /** * Verifies the account. * @param {Transaction} tx - The transaction. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise. */ - async verifyAccount(tx: Transaction, requestId?: string): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); - // verify account - const accountInfo = await this.mirrorNodeClient.getAccount(tx.from!, requestId); + async verifyAccount(tx: Transaction, requestDetails: RequestDetails): Promise { + const accountInfo = await this.mirrorNodeClient.getAccount(tx.from!, requestDetails); if (accountInfo == null) { this.logger.trace( - `${requestIdPrefix} Failed to retrieve address '${ + `${requestDetails.formattedRequestId} Failed to retrieve address '${ tx.from }' account details from mirror node on verify account precheck for sendRawTransaction(transaction=${JSON.stringify( tx, @@ -114,12 +113,11 @@ export class Precheck { * Checks the nonce of the transaction. * @param {Transaction} tx - The transaction. * @param {number} accountInfoNonce - The nonce of the account. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - nonce(tx: Transaction, accountInfoNonce: number, requestId?: string): void { - const requestIdPrefix = formatRequestIdMessage(requestId); + nonce(tx: Transaction, accountInfoNonce: number, requestDetails: RequestDetails): void { this.logger.trace( - `${requestIdPrefix} Nonce precheck for sendRawTransaction(tx.nonce=${tx.nonce}, accountInfoNonce=${accountInfoNonce})`, + `${requestDetails.formattedRequestId} Nonce precheck for sendRawTransaction(tx.nonce=${tx.nonce}, accountInfoNonce=${accountInfoNonce})`, ); if (accountInfoNonce > tx.nonce) { @@ -130,15 +128,14 @@ export class Precheck { /** * Checks the chain ID of the transaction. * @param {Transaction} tx - The transaction. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - chainId(tx: Transaction, requestId?: string): void { - const requestIdPrefix = formatRequestIdMessage(requestId); + chainId(tx: Transaction, requestDetails: RequestDetails): void { const txChainId = prepend0x(Number(tx.chainId).toString(16)); const passes = this.isLegacyUnprotectedEtx(tx) || txChainId === this.chain; if (!passes) { this.logger.trace( - `${requestIdPrefix} Failed chainId precheck for sendRawTransaction(transaction=%s, chainId=%s)`, + `${requestDetails.formattedRequestId} Failed chainId precheck for sendRawTransaction(transaction=%s, chainId=%s)`, JSON.stringify(tx), txChainId, ); @@ -163,8 +160,7 @@ export class Precheck { * @param {number} networkGasPriceInWeiBars - The predefined gas price of the network in weibar. * @param {string} [requestId] - The request ID. */ - gasPrice(tx: Transaction, networkGasPriceInWeiBars: number, requestId?: string): void { - const requestIdPrefix = formatRequestIdMessage(requestId); + gasPrice(tx: Transaction, networkGasPriceInWeiBars: number, requestDetails: RequestDetails): void { const networkGasPrice = BigInt(networkGasPriceInWeiBars); const txGasPrice = tx.gasPrice || tx.maxFeePerGas! + tx.maxPriorityFeePerGas!; @@ -186,7 +182,7 @@ export class Precheck { } this.logger.trace( - `${requestIdPrefix} Failed gas price precheck for sendRawTransaction(transaction=%s, gasPrice=%s, requiredGasPrice=%s)`, + `${requestDetails.formattedRequestId} Failed gas price precheck for sendRawTransaction(transaction=%s, gasPrice=%s, requiredGasPrice=%s)`, JSON.stringify(tx), txGasPrice, networkGasPrice, @@ -208,10 +204,9 @@ export class Precheck { * Checks the balance of the sender account. * @param {Transaction} tx - The transaction. * @param {any} account - The account information. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - balance(tx: Transaction, account: any, requestId?: string): void { - const requestIdPrefix = formatRequestIdMessage(requestId); + balance(tx: Transaction, account: any, requestDetails: RequestDetails): void { const result = { passes: false, error: predefined.INSUFFICIENT_ACCOUNT_BALANCE, @@ -221,7 +216,9 @@ export class Precheck { if (account == null) { this.logger.trace( - `${requestIdPrefix} Failed to retrieve account details from mirror node on balance precheck for sendRawTransaction(transaction=${JSON.stringify( + `${ + requestDetails.formattedRequestId + } Failed to retrieve account details from mirror node on balance precheck for sendRawTransaction(transaction=${JSON.stringify( tx, )}, totalValue=${txTotalValue})`, ); @@ -234,7 +231,7 @@ export class Precheck { result.passes = tinybars >= txTotalValue; } catch (error: any) { this.logger.trace( - `${requestIdPrefix} Error on balance precheck for sendRawTransaction(transaction=%s, totalValue=%s, error=%s)`, + `${requestDetails.formattedRequestId} Error on balance precheck for sendRawTransaction(transaction=%s, totalValue=%s, error=%s)`, JSON.stringify(tx), txTotalValue, error.message, @@ -249,7 +246,7 @@ export class Precheck { if (!result.passes) { this.logger.trace( - `${requestIdPrefix} Failed balance precheck for sendRawTransaction(transaction=%s, totalValue=%s, accountTinyBarBalance=%s)`, + `${requestDetails.formattedRequestId} Failed balance precheck for sendRawTransaction(transaction=%s, totalValue=%s, accountTinyBarBalance=%s)`, JSON.stringify(tx), txTotalValue, tinybars, @@ -261,10 +258,9 @@ export class Precheck { /** * Checks the gas limit of the transaction. * @param {Transaction} tx - The transaction. - * @param {string} [requestId] - The request ID. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - gasLimit(tx: Transaction, requestId?: string): void { - const requestIdPrefix = formatRequestIdMessage(requestId); + gasLimit(tx: Transaction, requestDetails: RequestDetails): void { const gasLimit = Number(tx.gasLimit); const failBaseLog = 'Failed gasLimit precheck for sendRawTransaction(transaction=%s).'; @@ -272,7 +268,7 @@ export class Precheck { if (gasLimit > constants.MAX_GAS_PER_SEC) { this.logger.trace( - `${requestIdPrefix} ${failBaseLog} Gas Limit was too high: %s, block gas limit: %s`, + `${requestDetails.formattedRequestId} ${failBaseLog} Gas Limit was too high: %s, block gas limit: %s`, JSON.stringify(tx), gasLimit, constants.MAX_GAS_PER_SEC, @@ -280,7 +276,7 @@ export class Precheck { throw predefined.GAS_LIMIT_TOO_HIGH(gasLimit, constants.MAX_GAS_PER_SEC); } else if (gasLimit < intrinsicGasCost) { this.logger.trace( - `${requestIdPrefix} ${failBaseLog} Gas Limit was too low: %s, intrinsic gas cost: %s`, + `${requestDetails.formattedRequestId} ${failBaseLog} Gas Limit was too low: %s, intrinsic gas cost: %s`, JSON.stringify(tx), gasLimit, intrinsicGasCost, @@ -349,12 +345,11 @@ export class Precheck { } } - transactionType(tx: Transaction, requestId?: string) { + transactionType(tx: Transaction, requestDetails: RequestDetails) { // Blob transactions are not supported as per HIP 866 if (tx.type === 3) { - const requestIdPrefix = formatRequestIdMessage(requestId); this.logger.trace( - `${requestIdPrefix} Transaction with type=${ + `${requestDetails.formattedRequestId} Transaction with type=${ tx.type } is unsupported for sendRawTransaction(transaction=${JSON.stringify(tx)})`, ); diff --git a/packages/relay/src/lib/relay.ts b/packages/relay/src/lib/relay.ts index e30b0232ee..b366e541c4 100644 --- a/packages/relay/src/lib/relay.ts +++ b/packages/relay/src/lib/relay.ts @@ -39,6 +39,8 @@ import HAPIService from './services/hapiService/hapiService'; import { SubscriptionController } from './subscriptionController'; import MetricService from './services/metricService/metricService'; import { CacheService } from './services/cacheService/cacheService'; +import { RequestDetails } from './types'; +import { Utils } from '../utils'; export class RelayImpl implements Relay { /** @@ -181,7 +183,7 @@ export class RelayImpl implements Relay { mirrorNodeClient: MirrorNodeClient, logger: Logger, register: Registry, - ) { + ): Gauge { const metricGaugeName = 'rpc_relay_operator_balance'; register.removeSingleMetric(metricGaugeName); return new Gauge({ @@ -193,7 +195,10 @@ export class RelayImpl implements Relay { // Invoked when the registry collects its metrics' values. // Allows for updated account balance tracking try { - const account = await mirrorNodeClient.getAccount(clientMain.operatorAccountId!.toString()); + const account = await mirrorNodeClient.getAccount( + clientMain.operatorAccountId!.toString(), + new RequestDetails({ requestId: Utils.generateRequestId(), ipAddress: '' }), + ); const accountBalance = account.balance?.balance; this.labels({ accountId: clientMain.operatorAccountId?.toString() }).set(accountBalance); } catch (e: any) { diff --git a/packages/relay/src/lib/services/cacheService/cacheService.ts b/packages/relay/src/lib/services/cacheService/cacheService.ts index 1d21fcacca..e1a9135403 100644 --- a/packages/relay/src/lib/services/cacheService/cacheService.ts +++ b/packages/relay/src/lib/services/cacheService/cacheService.ts @@ -23,6 +23,7 @@ import { Counter, Registry } from 'prom-client'; import { ICacheClient } from '../../clients/cache/ICacheClient'; import { LocalLRUCache, RedisCache } from '../../clients'; import { RedisCacheError } from '../../errors/RedisCacheError'; +import { RequestDetails } from '../../types'; /** * A service that manages caching using different cache implementations based on configuration. @@ -199,24 +200,25 @@ export class CacheService { * * @param {string} key - The cache key. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the cached value or null if not found. */ - private async getFromSharedCache(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + private async getFromSharedCache(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { try { this.cacheMethodsCounter .labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.GET_ASYNC) .inc(1); - return await this.sharedCache.get(key, callingMethod, requestIdPrefix); + return await this.sharedCache.get(key, callingMethod, requestDetails); } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while getting the cache from Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while getting the cache from Redis. Fallback to internal cache.`, ); // fallback to internal cache in case of Redis error - return await this.getFromInternalCache(key, callingMethod, requestIdPrefix); + return await this.getFromInternalCache(key, callingMethod, requestDetails); } } @@ -224,15 +226,15 @@ export class CacheService { * If SharedCacheEnabled will use shared, otherwise will fallback to internal cache. * @param {string} key - The cache key. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the cached value or null if not found. * @template T - The type of the cached value. */ - public async getAsync(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + public async getAsync(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { if (this.isSharedCacheEnabled) { - return await this.getFromSharedCache(key, callingMethod, requestIdPrefix); + return await this.getFromSharedCache(key, callingMethod, requestDetails); } else { - return await this.getFromInternalCache(key, callingMethod, requestIdPrefix); + return await this.getFromInternalCache(key, callingMethod, requestDetails); } } @@ -241,13 +243,13 @@ export class CacheService { * * @param {string} key - The cache key. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - The optional request ID prefix. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the cached value or null if not found. */ - private async getFromInternalCache(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + private async getFromInternalCache(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.GET).inc(1); - return await this.internalCache.get(key, callingMethod, requestIdPrefix); + return await this.internalCache.get(key, callingMethod, requestDetails); } /** @@ -258,32 +260,33 @@ export class CacheService { * @param {*} value - The value to cache. * @param {string} callingMethod - The name of the method calling the cache. * @param {number} ttl - Time to live for the cached value in milliseconds (optional). - * @param {string} requestIdPrefix - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ public async set( key: string, value: any, callingMethod: string, + requestDetails: RequestDetails, ttl?: number, - requestIdPrefix?: string, ): Promise { if (this.isSharedCacheEnabled) { try { this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.SET).inc(1); - await this.sharedCache.set(key, value, callingMethod, ttl, requestIdPrefix); + await this.sharedCache.set(key, value, callingMethod, requestDetails, ttl); return; } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while setting the cache to Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while setting the cache to Redis. Fallback to internal cache.`, ); } } // fallback to internal cache in case of Redis error this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.SET).inc(1); - await this.internalCache.set(key, value, callingMethod, ttl, requestIdPrefix); + await this.internalCache.set(key, value, callingMethod, requestDetails, ttl); } /** @@ -294,21 +297,21 @@ export class CacheService { * @param {Record} entries - An object containing key-value pairs to cache. * @param {string} callingMethod - The name of the method calling the cache. * @param {number} [ttl] - Time to live for the cached value in milliseconds (optional). - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ public async multiSet( entries: Record, callingMethod: string, + requestDetails: RequestDetails, ttl?: number, - requestIdPrefix?: string, ): Promise { if (this.isSharedCacheEnabled) { const metricsMethod = this.shouldMultiSet ? CacheService.methods.MSET : CacheService.methods.PIPELINE; try { if (this.shouldMultiSet) { - await this.sharedCache.multiSet(entries, callingMethod, requestIdPrefix); + await this.sharedCache.multiSet(entries, callingMethod, requestDetails); } else { - await this.sharedCache.pipelineSet(entries, callingMethod, ttl, requestIdPrefix); + await this.sharedCache.pipelineSet(entries, callingMethod, requestDetails, ttl); } this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.REDIS, metricsMethod).inc(1); @@ -316,13 +319,14 @@ export class CacheService { } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while setting the cache to Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while setting the cache to Redis. Fallback to internal cache.`, ); } } // fallback to internal cache, but use pipeline, because of it's TTL support - await this.internalCache.pipelineSet(entries, callingMethod, ttl, requestIdPrefix); + await this.internalCache.pipelineSet(entries, callingMethod, requestDetails, ttl); this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.SET).inc(1); } @@ -332,37 +336,38 @@ export class CacheService { * Else the internal cache deletion is attempted. * @param {string} key - The key associated with the cached value to delete. * @param {string} callingMethod - The name of the method calling the cache. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - public async delete(key: string, callingMethod: string, requestIdPrefix?: string): Promise { + public async delete(key: string, callingMethod: string, requestDetails: RequestDetails): Promise { if (this.isSharedCacheEnabled) { try { this.cacheMethodsCounter .labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.DELETE) .inc(1); - await this.sharedCache.delete(key, callingMethod, requestIdPrefix); + await this.sharedCache.delete(key, callingMethod, requestDetails); return; } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while deleting cache from Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while deleting cache from Redis.`, ); } } // fallback to internal cache in case of Redis error this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.DELETE).inc(1); - await this.internalCache.delete(key, callingMethod, requestIdPrefix); + await this.internalCache.delete(key, callingMethod, requestDetails); } /** * Clears the cache. * If the shared cache is enabled and an error occurs while clearing it, just logs the error. * Else the internal cache clearing is attempted. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ - public async clear(requestIdPrefix?: string): Promise { + public async clear(requestDetails: RequestDetails): Promise { if (this.isSharedCacheEnabled) { try { await this.sharedCache.clear(); @@ -370,7 +375,8 @@ export class CacheService { } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while clearing Redis cache. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while clearing Redis cache.`, ); } } @@ -384,35 +390,41 @@ export class CacheService { * @param {string} key - The key to increment. * @param {number} amount - The amount to increment by. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the new value of the key after incrementing. */ - public async incrBy(key: string, amount: number, callingMethod: string, requestIdPrefix?: string): Promise { + public async incrBy( + key: string, + amount: number, + callingMethod: string, + requestDetails: RequestDetails, + ): Promise { if (this.isSharedCacheEnabled && this.sharedCache instanceof RedisCache) { try { this.cacheMethodsCounter .labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.INCR_BY) .inc(1); - return await this.sharedCache.incrBy(key, amount, callingMethod, requestIdPrefix); + return await this.sharedCache.incrBy(key, amount, callingMethod, requestDetails); } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while incrementing cache in Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while incrementing cache in Redis.`, ); } } // Fallback to internal cache - const value = await this.getFromInternalCache(key, callingMethod, requestIdPrefix); + const value = await this.getFromInternalCache(key, callingMethod, requestDetails); const newValue = value + amount; const remainingTtl = this.internalCache instanceof LocalLRUCache - ? await this.internalCache.getRemainingTtl(key, callingMethod, requestIdPrefix) + ? await this.internalCache.getRemainingTtl(key, callingMethod, requestDetails) : undefined; this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.SET).inc(1); - await this.internalCache.set(key, newValue, callingMethod, remainingTtl, requestIdPrefix); + await this.internalCache.set(key, newValue, callingMethod, requestDetails, remainingTtl); return newValue; } @@ -422,38 +434,39 @@ export class CacheService { * @param {string} key - The key of the list. * @param {*} value - The value to push. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the new length of the list after pushing. */ - public async rPush(key: string, value: any, callingMethod: string, requestIdPrefix?: string): Promise { + public async rPush(key: string, value: any, callingMethod: string, requestDetails: RequestDetails): Promise { if (this.isSharedCacheEnabled && this.sharedCache instanceof RedisCache) { try { this.cacheMethodsCounter .labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.RPUSH) .inc(1); - return await this.sharedCache.rPush(key, value, callingMethod, requestIdPrefix); + return await this.sharedCache.rPush(key, value, callingMethod, requestDetails); } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while pushing cache in Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while pushing cache in Redis.`, ); } } // Fallback to internal cache - const values = (await this.getFromInternalCache(key, callingMethod, requestIdPrefix)) ?? []; + const values = (await this.getFromInternalCache(key, callingMethod, requestDetails)) ?? []; if (!Array.isArray(values)) { throw new Error(`Value at key ${key} is not an array`); } values.push(value); const remainingTtl = this.internalCache instanceof LocalLRUCache - ? await this.internalCache.getRemainingTtl(key, callingMethod, requestIdPrefix) + ? await this.internalCache.getRemainingTtl(key, callingMethod, requestDetails) : undefined; this.cacheMethodsCounter.labels(callingMethod, CacheService.cacheTypes.LRU, CacheService.methods.SET).inc(1); - await this.internalCache.set(key, values, callingMethod, remainingTtl, requestIdPrefix); + await this.internalCache.set(key, values, callingMethod, requestDetails, remainingTtl); return values.length; } @@ -464,7 +477,7 @@ export class CacheService { * @param {number} start - The start index of the range. * @param {number} end - The end index of the range. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with the values in the range. * @template T - The type of the values in the list. */ @@ -473,7 +486,7 @@ export class CacheService { start: number, end: number, callingMethod: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { if (this.isSharedCacheEnabled && this.sharedCache instanceof RedisCache) { try { @@ -481,17 +494,18 @@ export class CacheService { .labels(callingMethod, CacheService.cacheTypes.REDIS, CacheService.methods.LRANGE) .inc(1); - return await this.sharedCache.lRange(key, start, end, callingMethod, requestIdPrefix); + return await this.sharedCache.lRange(key, start, end, callingMethod, requestDetails); } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while getting cache in Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while getting cache in Redis.`, ); } } // Fallback to internal cache - const values = (await this.getFromInternalCache(key, callingMethod, requestIdPrefix)) ?? []; + const values = (await this.getFromInternalCache(key, callingMethod, requestDetails)) ?? []; if (!Array.isArray(values)) { throw new Error(`Value at key ${key} is not an array`); } @@ -505,22 +519,23 @@ export class CacheService { * Retrieves all keys matching the given pattern. * @param {string} pattern - The pattern to match keys against. * @param {string} callingMethod - The name of the calling method. - * @param {string} [requestIdPrefix] - A prefix to include in log messages (optional). + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A Promise that resolves with an array of keys that match the pattern. */ - async keys(pattern: string, callingMethod: string, requestIdPrefix?: string): Promise { + async keys(pattern: string, callingMethod: string, requestDetails: RequestDetails): Promise { if (this.isSharedCacheEnabled && this.sharedCache instanceof RedisCache) { try { - return await this.sharedCache.keys(pattern, callingMethod, requestIdPrefix); + return await this.sharedCache.keys(pattern, callingMethod, requestDetails); } catch (error) { const redisError = new RedisCacheError(error); this.logger.error( - `${requestIdPrefix} Error occurred while getting keys from Redis. Fallback to internal cache. ${redisError}`, + redisError, + `${requestDetails.formattedRequestId} Error occurred while getting keys from Redis.`, ); } } // Fallback to internal cache - return this.internalCache.keys(pattern, callingMethod, requestIdPrefix); + return this.internalCache.keys(pattern, callingMethod, requestDetails); } } diff --git a/packages/relay/src/lib/services/debugService/IDebugService.ts b/packages/relay/src/lib/services/debugService/IDebugService.ts index a57566ab49..0237eb5bf1 100644 --- a/packages/relay/src/lib/services/debugService/IDebugService.ts +++ b/packages/relay/src/lib/services/debugService/IDebugService.ts @@ -18,7 +18,7 @@ * */ -import { ITracerConfig } from '../../types'; +import { ITracerConfig, RequestDetails } from '../../types'; import type { TracerType } from '../../constants'; export interface IDebugService { @@ -26,7 +26,7 @@ export interface IDebugService { transactionIdOrHash: string, tracer: TracerType, tracerConfig: ITracerConfig, - requestIdPrefix?: string, + requestDetails: RequestDetails, ) => Promise; - resolveAddress: (address: string, types?: string[], requestIdPrefix?: string) => Promise; + resolveAddress: (address: string, requestDetails: RequestDetails, types?: string[]) => Promise; } diff --git a/packages/relay/src/lib/services/debugService/index.ts b/packages/relay/src/lib/services/debugService/index.ts index 6ee0b7ae51..6a57d47bbf 100644 --- a/packages/relay/src/lib/services/debugService/index.ts +++ b/packages/relay/src/lib/services/debugService/index.ts @@ -29,6 +29,7 @@ import { EthImpl } from '../../eth'; import { IOpcodesResponse } from '../../clients/models/IOpcodesResponse'; import { IOpcode } from '../../clients/models/IOpcode'; import { ICallTracerConfig, IOpcodeLoggerConfig, ITracerConfig } from '../../types'; +import { RequestDetails } from '../../types'; /** * Represents a DebugService for tracing and debugging transactions and debugging @@ -88,7 +89,7 @@ export class DebugService implements IDebugService { * @param {string} transactionIdOrHash - The ID or hash of the transaction to be traced. * @param {TracerType} tracer - The type of tracer to use (either 'CallTracer' or 'OpcodeLogger'). * @param {ITracerConfig} tracerConfig - The configuration object for the tracer. - * @param {string} [requestIdPrefix] - An optional request id. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @throws {Error} Throws an error if the specified tracer type is not supported or if an exception occurs during the trace. * @returns {Promise} A Promise that resolves to the result of the trace operation. * @@ -99,15 +100,15 @@ export class DebugService implements IDebugService { transactionIdOrHash: string, tracer: TracerType, tracerConfig: ITracerConfig, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { - this.logger.trace(`${requestIdPrefix} debug_traceTransaction(${transactionIdOrHash})`); + this.logger.trace(`${requestDetails.formattedRequestId} debug_traceTransaction(${transactionIdOrHash})`); try { DebugService.requireDebugAPIEnabled(); if (tracer === TracerType.CallTracer) { - return await this.callTracer(transactionIdOrHash, tracerConfig as ICallTracerConfig, requestIdPrefix); + return await this.callTracer(transactionIdOrHash, tracerConfig as ICallTracerConfig, requestDetails); } else if (tracer === TracerType.OpcodeLogger) { - return await this.callOpcodeLogger(transactionIdOrHash, tracerConfig as IOpcodeLoggerConfig, requestIdPrefix); + return await this.callOpcodeLogger(transactionIdOrHash, tracerConfig as IOpcodeLoggerConfig, requestDetails); } } catch (e) { throw this.common.genericErrorHandler(e); @@ -119,23 +120,23 @@ export class DebugService implements IDebugService { * * @async * @param {any} result - The response from the actions endpoint. - * @param {string} requestIdPrefix - The request prefix id. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise<[] | any>} The formatted actions response in an array. */ - async formatActionsResult(result: any, requestIdPrefix?: string): Promise<[] | any> { + async formatActionsResult(result: any, requestDetails: RequestDetails): Promise<[] | any> { return await Promise.all( result.map(async (action, index) => { const { resolvedFrom, resolvedTo } = await this.resolveMultipleAddresses( action.from, action.to, - requestIdPrefix, + requestDetails, ); // The actions endpoint does not return input and output for the calls so we get them from another endpoint // The first one is excluded because we take its input and output from the contracts/results/{transactionIdOrHash} endpoint const contract = index !== 0 && action.call_operation_type === CallType.CREATE - ? await this.mirrorNodeClient.getContract(action.to, requestIdPrefix) + ? await this.mirrorNodeClient.getContract(action.to, requestDetails) : undefined; return { @@ -201,22 +202,22 @@ export class DebugService implements IDebugService { * @async * @param {string} address - The address to be resolved. * @param {[string]} types - The possible types of the address. - * @param {string} requestIdPrefix - The request prefix id. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The address returned as an EVM address. */ async resolveAddress( address: string, - types = [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT], - requestIdPrefix?: string, + requestDetails: RequestDetails, + types: string[] = [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT], ): Promise { // if the address is null or undefined we return it as is if (!address) return address; const entity = await this.mirrorNodeClient.resolveEntityType( address, - types, EthImpl.debugTraceTransaction, - requestIdPrefix, + requestDetails, + types, ); if ( @@ -233,15 +234,15 @@ export class DebugService implements IDebugService { async resolveMultipleAddresses( from: string, to: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise<{ resolvedFrom: string; resolvedTo: string }> { const [resolvedFrom, resolvedTo] = await Promise.all([ - this.resolveAddress( - from, - [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT], - requestIdPrefix, - ), - this.resolveAddress(to, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT], requestIdPrefix), + this.resolveAddress(from, requestDetails, [ + constants.TYPE_CONTRACT, + constants.TYPE_TOKEN, + constants.TYPE_ACCOUNT, + ]), + this.resolveAddress(to, requestDetails, [constants.TYPE_CONTRACT, constants.TYPE_TOKEN, constants.TYPE_ACCOUNT]), ]); return { resolvedFrom, resolvedTo }; @@ -255,13 +256,13 @@ export class DebugService implements IDebugService { * @param {boolean} tracerConfig.enableMemory - Whether to enable memory. * @param {boolean} tracerConfig.disableStack - Whether to disable stack. * @param {boolean} tracerConfig.disableStorage - Whether to disable storage. - * @param {string} requestIdPrefix - The request prefix id. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The formatted response. */ async callOpcodeLogger( transactionIdOrHash: string, tracerConfig: IOpcodeLoggerConfig, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { try { const options = { @@ -271,7 +272,7 @@ export class DebugService implements IDebugService { }; const response = await this.mirrorNodeClient.getContractsResultsOpcodes( transactionIdOrHash, - requestIdPrefix, + requestDetails, options, ); return await this.formatOpcodesResult(response, options); @@ -286,25 +287,25 @@ export class DebugService implements IDebugService { * @async * @param {string} transactionHash - The hash of the transaction to be debugged. * @param {ICallTracerConfig} tracerConfig - The tracer config to be used. - * @param {string} requestIdPrefix - The request prefix id. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} The formatted response. */ async callTracer( transactionHash: string, tracerConfig: ICallTracerConfig, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { try { const [actionsResponse, transactionsResponse] = await Promise.all([ - this.mirrorNodeClient.getContractsResultsActions(transactionHash, requestIdPrefix), - this.mirrorNodeClient.getContractResultWithRetry(transactionHash), + this.mirrorNodeClient.getContractsResultsActions(transactionHash, requestDetails), + this.mirrorNodeClient.getContractResultWithRetry(transactionHash, requestDetails), ]); if (!actionsResponse || !transactionsResponse) { throw predefined.RESOURCE_NOT_FOUND(`Failed to retrieve contract results for transaction ${transactionHash}`); } const { call_type: type } = actionsResponse.actions[0]; - const formattedActions = await this.formatActionsResult(actionsResponse.actions, requestIdPrefix); + const formattedActions = await this.formatActionsResult(actionsResponse.actions, requestDetails); const { from, @@ -318,7 +319,7 @@ export class DebugService implements IDebugService { result, } = transactionsResponse; - const { resolvedFrom, resolvedTo } = await this.resolveMultipleAddresses(from, to, requestIdPrefix); + const { resolvedFrom, resolvedTo } = await this.resolveMultipleAddresses(from, to, requestDetails); const value = amount === 0 ? EthImpl.zeroHex : numberTo0x(amount); const errorResult = result !== constants.SUCCESS ? result : undefined; diff --git a/packages/relay/src/lib/services/ethService/ethCommonService/ICommonService.ts b/packages/relay/src/lib/services/ethService/ethCommonService/ICommonService.ts index 6587c96fb7..f0873fc8e3 100644 --- a/packages/relay/src/lib/services/ethService/ethCommonService/ICommonService.ts +++ b/packages/relay/src/lib/services/ethService/ethCommonService/ICommonService.ts @@ -18,6 +18,7 @@ * */ import { Log } from '../../../model'; +import { RequestDetails } from '../../../types'; export interface ICommonService { blockTagIsLatestOrPending(tag: any): boolean; @@ -26,27 +27,31 @@ export interface ICommonService { params: any, fromBlock: string, toBlock: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, address?: string | string[] | null, ): Promise; getHistoricalBlockResponse( + requestDetails: RequestDetails, blockNumberOrTag?: string | null, returnLatest?: boolean, - requestIdPrefix?: string | undefined, ): Promise; - getLatestBlockNumber(requestIdPrefix?: string): Promise; + getLatestBlockNumber(requestDetails: RequestDetails): Promise; genericErrorHandler(error: any, logMessage?: string): void; - validateBlockHashAndAddTimestampToParams(params: any, blockHash: string, requestIdPrefix?: string): Promise; + validateBlockHashAndAddTimestampToParams( + params: any, + blockHash: string, + requestDetails: RequestDetails, + ): Promise; addTopicsToParams(params: any, topics: any[] | null): void; - getLogsByAddress(address: string | [string], params: any, requestIdPrefix): Promise; + getLogsByAddress(address: string | [string], params: any, requestDetails: RequestDetails): Promise; - getLogsWithParams(address: string | [string] | null, params, requestIdPrefix?: string): Promise; + getLogsWithParams(address: string | [string] | null, params: any, requestDetails: RequestDetails): Promise; getLogs( blockHash: string | null, @@ -54,6 +59,6 @@ export interface ICommonService { toBlock: string | 'latest', address: string | [string] | null, topics: any[] | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise; } diff --git a/packages/relay/src/lib/services/ethService/ethCommonService/index.ts b/packages/relay/src/lib/services/ethService/ethCommonService/index.ts index 3439c53f9b..3e8ff7d084 100644 --- a/packages/relay/src/lib/services/ethService/ethCommonService/index.ts +++ b/packages/relay/src/lib/services/ethService/ethCommonService/index.ts @@ -29,6 +29,7 @@ import { MirrorNodeClientError } from '../../../errors/MirrorNodeClientError'; import { Log } from '../../../model'; import * as _ from 'lodash'; import { CacheService } from '../../cacheService/cacheService'; +import { RequestDetails } from '../../../types'; /** * Create a new Common Service implementation. @@ -102,14 +103,14 @@ export class CommonService implements ICommonService { params: any, fromBlock: string, toBlock: string, - requestIdPrefix?: string, + requestDetails: RequestDetails, address?: string | string[] | null, ) { if (this.blockTagIsLatestOrPending(toBlock)) { toBlock = CommonService.blockLatest; } - const latestBlockNumber: string = await this.getLatestBlockNumber(requestIdPrefix); + const latestBlockNumber: string = await this.getLatestBlockNumber(requestDetails); // toBlock is a number and is less than the current block number and fromBlock is not defined if (Number(toBlock) < Number(latestBlockNumber) && !fromBlock) { @@ -124,7 +125,7 @@ export class CommonService implements ICommonService { let toBlockNum; params.timestamp = []; - const fromBlockResponse = await this.getHistoricalBlockResponse(fromBlock, true, requestIdPrefix); + const fromBlockResponse = await this.getHistoricalBlockResponse(requestDetails, fromBlock, true); if (!fromBlockResponse) { return false; } @@ -135,7 +136,7 @@ export class CommonService implements ICommonService { params.timestamp.push(`lte:${fromBlockResponse.timestamp.to}`); } else { fromBlockNum = parseInt(fromBlockResponse.number); - const toBlockResponse = await this.getHistoricalBlockResponse(toBlock, true, requestIdPrefix); + const toBlockResponse = await this.getHistoricalBlockResponse(requestDetails, toBlock, true); if (toBlockResponse != null) { params.timestamp.push(`lte:${toBlockResponse.timestamp.to}`); toBlockNum = parseInt(toBlockResponse.number); @@ -163,13 +164,14 @@ export class CommonService implements ICommonService { * returns the block response * otherwise return undefined. * - * @param blockNumberOrTag + * @param requestDetails + * @param blockNumberOrTagOrHash * @param returnLatest */ public async getHistoricalBlockResponse( + requestDetails: RequestDetails, blockNumberOrTagOrHash?: string | null, returnLatest?: boolean, - requestIdPrefix?: string | undefined, ): Promise { if (!returnLatest && this.blockTagIsLatestOrPending(blockNumberOrTagOrHash)) { return null; @@ -177,7 +179,7 @@ export class CommonService implements ICommonService { const blockNumber = Number(blockNumberOrTagOrHash); if (blockNumberOrTagOrHash != null && blockNumberOrTagOrHash.length < 32 && !isNaN(blockNumber)) { - const latestBlockResponse = await this.mirrorNodeClient.getLatestBlock(requestIdPrefix); + const latestBlockResponse = await this.mirrorNodeClient.getLatestBlock(requestDetails); const latestBlock = latestBlockResponse.blocks[0]; if (blockNumber > latestBlock.number + this.maxBlockRange) { return null; @@ -185,39 +187,41 @@ export class CommonService implements ICommonService { } if (blockNumberOrTagOrHash == null || this.blockTagIsLatestOrPending(blockNumberOrTagOrHash)) { - const latestBlockResponse = await this.mirrorNodeClient.getLatestBlock(requestIdPrefix); + const latestBlockResponse = await this.mirrorNodeClient.getLatestBlock(requestDetails); return latestBlockResponse.blocks[0]; } if (blockNumberOrTagOrHash == CommonService.blockEarliest) { - return await this.mirrorNodeClient.getBlock(0, requestIdPrefix); + return await this.mirrorNodeClient.getBlock(0, requestDetails); } if (blockNumberOrTagOrHash.length < 32) { - return await this.mirrorNodeClient.getBlock(Number(blockNumberOrTagOrHash), requestIdPrefix); + return await this.mirrorNodeClient.getBlock(Number(blockNumberOrTagOrHash), requestDetails); } - return await this.mirrorNodeClient.getBlock(blockNumberOrTagOrHash, requestIdPrefix); + return await this.mirrorNodeClient.getBlock(blockNumberOrTagOrHash, requestDetails); } /** * Gets the most recent block number. */ - public async getLatestBlockNumber(requestIdPrefix?: string): Promise { + public async getLatestBlockNumber(requestDetails: RequestDetails): Promise { // check for cached value const cacheKey = `${constants.CACHE_KEY.ETH_BLOCK_NUMBER}`; const blockNumberCached = await this.cacheService.getAsync( cacheKey, CommonService.latestBlockNumber, - requestIdPrefix, + requestDetails, ); if (blockNumberCached) { - this.logger.trace(`${requestIdPrefix} returning cached value ${cacheKey}:${JSON.stringify(blockNumberCached)}`); + this.logger.trace( + `${requestDetails.formattedRequestId} returning cached value ${cacheKey}:${JSON.stringify(blockNumberCached)}`, + ); return blockNumberCached; } - const blocksResponse = await this.mirrorNodeClient.getLatestBlock(requestIdPrefix); + const blocksResponse = await this.mirrorNodeClient.getLatestBlock(requestDetails); const blocks = blocksResponse !== null ? blocksResponse.blocks : null; if (Array.isArray(blocks) && blocks.length > 0) { const currentBlock = numberTo0x(blocks[0].number); @@ -226,8 +230,8 @@ export class CommonService implements ICommonService { cacheKey, currentBlock, CommonService.latestBlockNumber, + requestDetails, this.ethBlockNumberCacheTtlMs, - requestIdPrefix, ); return currentBlock; @@ -253,9 +257,13 @@ export class CommonService implements ICommonService { throw predefined.INTERNAL_ERROR(error.message.toString()); } - public async validateBlockHashAndAddTimestampToParams(params: any, blockHash: string, requestIdPrefix?: string) { + public async validateBlockHashAndAddTimestampToParams( + params: any, + blockHash: string, + requestDetails: RequestDetails, + ) { try { - const block = await this.mirrorNodeClient.getBlock(blockHash, requestIdPrefix); + const block = await this.mirrorNodeClient.getBlock(blockHash, requestDetails); if (block) { params.timestamp = [`gte:${block.timestamp.from}`, `lte:${block.timestamp.to}`]; } else { @@ -282,10 +290,10 @@ export class CommonService implements ICommonService { } } - public async getLogsByAddress(address: string | string[], params: any, requestIdPrefix) { + public async getLogsByAddress(address: string | string[], params: any, requestDetails: RequestDetails) { const addresses = Array.isArray(address) ? address : [address]; const logPromises = addresses.map((addr) => - this.mirrorNodeClient.getContractResultsLogsByAddress(addr, params, undefined, requestIdPrefix), + this.mirrorNodeClient.getContractResultsLogsByAddress(addr, requestDetails, params, undefined), ); const logResults = await Promise.all(logPromises); @@ -297,14 +305,18 @@ export class CommonService implements ICommonService { return logs; } - public async getLogsWithParams(address: string | string[] | null, params, requestIdPrefix?: string): Promise { + public async getLogsWithParams( + address: string | string[] | null, + params: any, + requestDetails: RequestDetails, + ): Promise { const EMPTY_RESPONSE = []; let logResults; if (address) { - logResults = await this.getLogsByAddress(address, params, requestIdPrefix); + logResults = await this.getLogsByAddress(address, params, requestDetails); } else { - logResults = await this.mirrorNodeClient.getContractResultsLogs(params, undefined, requestIdPrefix); + logResults = await this.mirrorNodeClient.getContractResultsLogs(requestDetails, params); } if (!logResults) { @@ -337,23 +349,23 @@ export class CommonService implements ICommonService { toBlock: string | 'latest', address: string | string[] | null, topics: any[] | null, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { const EMPTY_RESPONSE = []; const params: any = {}; if (blockHash) { - if (!(await this.validateBlockHashAndAddTimestampToParams(params, blockHash, requestIdPrefix))) { + if (!(await this.validateBlockHashAndAddTimestampToParams(params, blockHash, requestDetails))) { return EMPTY_RESPONSE; } } else if ( - !(await this.validateBlockRangeAndAddTimestampToParams(params, fromBlock, toBlock, requestIdPrefix, address)) + !(await this.validateBlockRangeAndAddTimestampToParams(params, fromBlock, toBlock, requestDetails, address)) ) { return EMPTY_RESPONSE; } this.addTopicsToParams(params, topics); - return this.getLogsWithParams(address, params, requestIdPrefix); + return this.getLogsWithParams(address, params, requestDetails); } } diff --git a/packages/relay/src/lib/services/ethService/ethFilterService/IFilterService.ts b/packages/relay/src/lib/services/ethService/ethFilterService/IFilterService.ts index efa42823d8..169d592e73 100644 --- a/packages/relay/src/lib/services/ethService/ethFilterService/IFilterService.ts +++ b/packages/relay/src/lib/services/ethService/ethFilterService/IFilterService.ts @@ -20,23 +20,24 @@ import { JsonRpcError } from '../../../errors/JsonRpcError'; import { Log } from '../../../model'; +import { RequestDetails } from '../../../types'; export interface IFilterService { newFilter( fromBlock: string, toBlock: string, + requestDetails: RequestDetails, address?: string, topics?: any[], - requestIdPrefix?: string, ): Promise; - newBlockFilter(requestIdPrefix?: string): Promise; + newBlockFilter(requestDetails: RequestDetails): Promise; - uninstallFilter(filterId: string, requestId?: string): Promise; + uninstallFilter(filterId: string, requestDetails: RequestDetails): Promise; - newPendingTransactionFilter(requestIdPrefix?: string): JsonRpcError; + newPendingTransactionFilter(requestDetails: RequestDetails): JsonRpcError; - getFilterLogs(filterId: string, requestId?: string): Promise; + getFilterLogs(filterId: string, requestDetails: RequestDetails): Promise; - getFilterChanges(filterId: string, requestIdPrefix?: string): Promise; + getFilterChanges(filterId: string, requestDetails: RequestDetails): Promise; } diff --git a/packages/relay/src/lib/services/ethService/ethFilterService/index.ts b/packages/relay/src/lib/services/ethService/ethFilterService/index.ts index 1cde8120ad..1e4bd60079 100644 --- a/packages/relay/src/lib/services/ethService/ethFilterService/index.ts +++ b/packages/relay/src/lib/services/ethService/ethFilterService/index.ts @@ -22,11 +22,12 @@ import { Logger } from 'pino'; import { MirrorNodeClient } from '../../../clients'; import constants from '../../../constants'; import { IFilterService } from './IFilterService'; -import { CommonService } from './../ethCommonService'; +import { CommonService } from '../ethCommonService'; import { generateRandomHex } from '../../../../formatters'; import { JsonRpcError, predefined } from '../../../errors/JsonRpcError'; import { Log } from '../../../model'; import { CacheService } from '../../cacheService/cacheService'; +import { RequestDetails } from '../../../types'; /** * Create a new Filter Service implementation. @@ -61,7 +62,7 @@ export class FilterService implements IFilterService { public readonly ethGetFilterChanges = 'eth_getFilterChanges'; private readonly common: CommonService; - private readonly supportedTypes; + private readonly supportedTypes: string[]; constructor(mirrorNodeClient: MirrorNodeClient, logger: Logger, cacheService: CacheService, common: CommonService) { this.mirrorNodeClient = mirrorNodeClient; @@ -76,9 +77,9 @@ export class FilterService implements IFilterService { * Creates a new filter with the specified type and parameters * @param type * @param params - * @param requestIdPrefix + * @param requestDetails */ - async createFilter(type: string, params: any, requestIdPrefix?: string): Promise { + async createFilter(type: string, params: any, requestDetails: RequestDetails): Promise { const filterId = generateRandomHex(); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; await this.cacheService.set( @@ -89,10 +90,10 @@ export class FilterService implements IFilterService { lastQueried: null, }, this.ethNewFilter, + requestDetails, constants.FILTER.TTL, - requestIdPrefix, ); - this.logger.trace(`${requestIdPrefix} created filter with TYPE=${type}, params: ${params}`); + this.logger.trace(`${requestDetails.formattedRequestId} created filter with TYPE=${type}, params: ${params}`); return filterId; } @@ -111,15 +112,16 @@ export class FilterService implements IFilterService { * @param toBlock * @param address * @param topics - * @param requestIdPrefix + * @param requestDetails */ async newFilter( fromBlock: string = 'latest', toBlock: string = 'latest', + requestDetails: RequestDetails, address?: string, topics?: any[], - requestIdPrefix?: string, - ): Promise { + ): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace( `${requestIdPrefix} newFilter(fromBlock=${fromBlock}, toBlock=${toBlock}, address=${address}, topics=${topics})`, ); @@ -127,7 +129,7 @@ export class FilterService implements IFilterService { FilterService.requireFiltersEnabled(); if ( - !(await this.common.validateBlockRangeAndAddTimestampToParams({}, fromBlock, toBlock, requestIdPrefix, address)) + !(await this.common.validateBlockRangeAndAddTimestampToParams({}, fromBlock, toBlock, requestDetails, address)) ) { throw predefined.INVALID_BLOCK_RANGE; } @@ -135,60 +137,62 @@ export class FilterService implements IFilterService { return await this.createFilter( constants.FILTER.TYPE.LOG, { - fromBlock: fromBlock === 'latest' ? await this.common.getLatestBlockNumber(requestIdPrefix) : fromBlock, + fromBlock: fromBlock === 'latest' ? await this.common.getLatestBlockNumber(requestDetails) : fromBlock, toBlock, address, topics, }, - requestIdPrefix, + requestDetails, ); } catch (e) { throw this.common.genericErrorHandler(e); } } - async newBlockFilter(requestIdPrefix?: string): Promise { + async newBlockFilter(requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} newBlockFilter()`); try { FilterService.requireFiltersEnabled(); return await this.createFilter( constants.FILTER.TYPE.NEW_BLOCK, { - blockAtCreation: await this.common.getLatestBlockNumber(requestIdPrefix), + blockAtCreation: await this.common.getLatestBlockNumber(requestDetails), }, - requestIdPrefix, + requestDetails, ); } catch (e) { throw this.common.genericErrorHandler(e); } } - public async uninstallFilter(filterId: string, requestIdPrefix?: string | undefined): Promise { + public async uninstallFilter(filterId: string, requestDetails: RequestDetails): Promise { + const requestIdPrefix = requestDetails.formattedRequestId; this.logger.trace(`${requestIdPrefix} uninstallFilter(${filterId})`); FilterService.requireFiltersEnabled(); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; - const filter = await this.cacheService.getAsync(cacheKey, this.ethUninstallFilter, requestIdPrefix); + const filter = await this.cacheService.getAsync(cacheKey, this.ethUninstallFilter, requestDetails); if (filter) { - await this.cacheService.delete(cacheKey, this.ethUninstallFilter, requestIdPrefix); + await this.cacheService.delete(cacheKey, this.ethUninstallFilter, requestDetails); return true; } return false; } - public newPendingTransactionFilter(requestIdPrefix?: string | undefined): JsonRpcError { - this.logger.trace(`${requestIdPrefix} newPendingTransactionFilter()`); + public newPendingTransactionFilter(requestDetails: RequestDetails): JsonRpcError { + this.logger.trace(`${requestDetails.formattedRequestId} newPendingTransactionFilter()`); return predefined.UNSUPPORTED_METHOD; } - public async getFilterLogs(filterId: string, requestIdPrefix?: string | undefined): Promise { - this.logger.trace(`${requestIdPrefix} getFilterLogs(${filterId})`); + public async getFilterLogs(filterId: string, requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getFilterLogs(${filterId})`); FilterService.requireFiltersEnabled(); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; - const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterLogs, requestIdPrefix); + const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterLogs, requestDetails); if (filter?.type != constants.FILTER.TYPE.LOG) { throw predefined.FILTER_NOT_FOUND; } @@ -199,16 +203,16 @@ export class FilterService implements IFilterService { filter?.params.toBlock, filter?.params.address, filter?.params.topics, - requestIdPrefix, + requestDetails, ); } - public async getFilterChanges(filterId: string, requestIdPrefix?: string): Promise { - this.logger.trace(`${requestIdPrefix} getFilterChanges(${filterId})`); + public async getFilterChanges(filterId: string, requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} getFilterChanges(${filterId})`); FilterService.requireFiltersEnabled(); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; - const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterChanges, requestIdPrefix); + const filter = await this.cacheService.getAsync(cacheKey, this.ethGetFilterChanges, requestDetails); if (!filter) { throw predefined.FILTER_NOT_FOUND; @@ -222,7 +226,7 @@ export class FilterService implements IFilterService { filter?.params.toBlock, filter?.params.address, filter?.params.topics, - requestIdPrefix, + requestDetails, ); // get the latest block number and add 1 to exclude current results from the next response because @@ -231,10 +235,11 @@ export class FilterService implements IFilterService { Number( result.length ? result[result.length - 1].blockNumber - : await this.common.getLatestBlockNumber(requestIdPrefix), + : await this.common.getLatestBlockNumber(requestDetails), ) + 1; } else if (filter.type === constants.FILTER.TYPE.NEW_BLOCK) { result = await this.mirrorNodeClient.getBlocks( + requestDetails, [`gt:${filter.lastQueried || filter.params.blockAtCreation}`], undefined, { @@ -245,7 +250,7 @@ export class FilterService implements IFilterService { latestBlockNumber = Number( result?.blocks?.length ? result.blocks[result.blocks.length - 1].number - : await this.common.getLatestBlockNumber(requestIdPrefix), + : await this.common.getLatestBlockNumber(requestDetails), ); result = result?.blocks?.map((r) => r.hash) || []; @@ -262,8 +267,8 @@ export class FilterService implements IFilterService { lastQueried: latestBlockNumber, }, this.ethGetFilterChanges, + requestDetails, constants.FILTER.TTL, - requestIdPrefix, ); return result; diff --git a/packages/relay/src/lib/services/hbarLimitService/IHbarLimitService.ts b/packages/relay/src/lib/services/hbarLimitService/IHbarLimitService.ts index ef0da42e57..b8f1f49fe1 100644 --- a/packages/relay/src/lib/services/hbarLimitService/IHbarLimitService.ts +++ b/packages/relay/src/lib/services/hbarLimitService/IHbarLimitService.ts @@ -18,15 +18,16 @@ * */ +import { RequestDetails } from '../../types'; + export interface IHbarLimitService { - resetLimiter(): Promise; + resetLimiter(requestDetails: RequestDetails): Promise; shouldLimit( mode: string, methodName: string, ethAddress: string, - ipAddress?: string, - requestId?: string, + requestDetails: RequestDetails, estimatedTxFee?: number, ): Promise; - addExpense(cost: number, ethAddress: string, ipAddress?: string): Promise; + addExpense(cost: number, ethAddress: string, requestDetails: RequestDetails, ipAddress?: string): Promise; } diff --git a/packages/relay/src/lib/services/hbarLimitService/index.ts b/packages/relay/src/lib/services/hbarLimitService/index.ts index 8a0ec03b83..dd4280b97a 100644 --- a/packages/relay/src/lib/services/hbarLimitService/index.ts +++ b/packages/relay/src/lib/services/hbarLimitService/index.ts @@ -21,12 +21,12 @@ import { Logger } from 'pino'; import { Counter, Gauge, Registry } from 'prom-client'; import { IHbarLimitService } from './IHbarLimitService'; -import { formatRequestIdMessage } from '../../../formatters'; import { SubscriptionType } from '../../db/types/hbarLimiter/subscriptionType'; import { IDetailedHbarSpendingPlan } from '../../db/types/hbarLimiter/hbarSpendingPlan'; import { HbarSpendingPlanRepository } from '../../db/repositories/hbarLimiter/hbarSpendingPlanRepository'; import { EthAddressHbarSpendingPlanRepository } from '../../db/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository'; import { IPAddressHbarSpendingPlanRepository } from '../../db/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository'; +import { RequestDetails } from '../../types/RequestDetails'; export class HbarLimitService implements IHbarLimitService { static readonly ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000; @@ -143,15 +143,14 @@ export class HbarLimitService implements IHbarLimitService { * @param {string} [requestId] - An optional unique request ID for tracking the request. * @returns {Promise} - A promise that resolves when the operation is complete. */ - async resetLimiter(requestId?: string): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); - this.logger.trace(`${requestIdPrefix} Resetting HBAR rate limiter...`); - await this.hbarSpendingPlanRepository.resetAllSpentTodayEntries(); + async resetLimiter(requestDetails: RequestDetails): Promise { + this.logger.trace(`${requestDetails.formattedRequestId} Resetting HBAR rate limiter...`); + await this.hbarSpendingPlanRepository.resetAllSpentTodayEntries(requestDetails); this.resetBudget(); this.resetMetrics(); this.reset = this.getResetTimestamp(); this.logger.trace( - `${requestIdPrefix} HBAR Rate Limit reset: remainingBudget=${this.remainingBudget}, newResetTimestamp=${this.reset}`, + `${requestDetails.formattedRequestId} HBAR Rate Limit reset: remainingBudget=${this.remainingBudget}, newResetTimestamp=${this.reset}`, ); } @@ -161,20 +160,19 @@ export class HbarLimitService implements IHbarLimitService { * @param {string} methodName - The name of the method being invoked. * @param {string} ethAddress - The eth address to check. * @param {string} [ipAddress] - The ip address to check. - * @param {string} [requestId] - A prefix to include in log messages (optional). * @param {number} [estimatedTxFee] - The total estimated transaction fee, default to 0. + * @param {RequestDetails} requestDetails The request details for logging and tracking. * @returns {Promise} - A promise that resolves with a boolean indicating if the address should be limited. */ async shouldLimit( mode: string, methodName: string, ethAddress: string, - ipAddress?: string, - requestId?: string, + requestDetails: RequestDetails, estimatedTxFee: number = 0, ): Promise { - const requestIdPrefix = formatRequestIdMessage(requestId); - if (await this.isDailyBudgetExceeded(mode, methodName, estimatedTxFee, requestIdPrefix)) { + const ipAddress = requestDetails.ipAddress; + if (await this.isDailyBudgetExceeded(mode, methodName, estimatedTxFee, requestDetails)) { return true; } if (!ethAddress && !ipAddress) { @@ -182,18 +180,18 @@ export class HbarLimitService implements IHbarLimitService { return false; } const user = `(ethAddress=${ethAddress}, ipAddress=${ipAddress})`; - this.logger.trace(`${requestIdPrefix} Checking if ${user} should be limited...`); - let spendingPlan = await this.getSpendingPlan(ethAddress, ipAddress); + this.logger.trace(`${requestDetails.formattedRequestId} Checking if ${user} should be limited...`); + let spendingPlan = await this.getSpendingPlan(ethAddress, requestDetails); if (!spendingPlan) { // Create a basic spending plan if none exists for the eth address or ip address - spendingPlan = await this.createBasicSpendingPlan(ethAddress, ipAddress); + spendingPlan = await this.createBasicSpendingPlan(ethAddress, requestDetails); } const dailyLimit = HbarLimitService.DAILY_LIMITS[spendingPlan.subscriptionType]; const exceedsLimit = spendingPlan.spentToday >= dailyLimit || spendingPlan.spentToday + estimatedTxFee > dailyLimit; this.logger.trace( - `${requestIdPrefix} ${user} ${exceedsLimit ? 'should' : 'should not'} be limited: spentToday=${ + `${requestDetails.formattedRequestId} ${user} ${exceedsLimit ? 'should' : 'should not'} be limited, spentToday=${ spendingPlan.spentToday }, estimatedTxFee=${estimatedTxFee}, dailyLimit=${dailyLimit}`, ); @@ -204,26 +202,26 @@ export class HbarLimitService implements IHbarLimitService { * Add expense to the remaining budget. * @param {number} cost - The cost of the expense. * @param {string} ethAddress - The Ethereum address to add the expense to. - * @param {string} [ipAddress] - The optional IP address to add the expense to. - * @param {string} [requestId] - An optional unique request ID for tracking the request. + * @param {string} ipAddress - The optional IP address to add the expense to. + * @param {RequestDetails} requestDetails The request details for logging and tracking. * @returns {Promise} - A promise that resolves when the expense has been added. */ - async addExpense(cost: number, ethAddress: string, ipAddress?: string, requestId?: string): Promise { + async addExpense(cost: number, ethAddress: string, requestDetails: RequestDetails): Promise { + const ipAddress = requestDetails.ipAddress; if (!ethAddress && !ipAddress) { throw new Error('Cannot add expense without an eth address or ip address'); } - let spendingPlan = await this.getSpendingPlan(ethAddress, ipAddress); + let spendingPlan = await this.getSpendingPlan(ethAddress, requestDetails); if (!spendingPlan) { // Create a basic spending plan if none exists for the eth address or ip address - spendingPlan = await this.createBasicSpendingPlan(ethAddress, ipAddress); + spendingPlan = await this.createBasicSpendingPlan(ethAddress, requestDetails); } - const requestIdPrefix = formatRequestIdMessage(requestId); this.logger.trace( - `${requestIdPrefix} Adding expense of ${cost} to spending plan with ID ${spendingPlan.id}, new spentToday=${ - spendingPlan.spentToday + cost - }`, + `${requestDetails.formattedRequestId} Adding expense of ${cost} to spending plan with ID ${ + spendingPlan.id + }, new spentToday=${spendingPlan.spentToday + cost}`, ); // Check if the spending plan is being used for the first time today @@ -231,15 +229,15 @@ export class HbarLimitService implements IHbarLimitService { this.dailyUniqueSpendingPlansCounter[spendingPlan.subscriptionType].inc(1); } - await this.hbarSpendingPlanRepository.addAmountToSpentToday(spendingPlan.id, cost); + await this.hbarSpendingPlanRepository.addAmountToSpentToday(spendingPlan.id, cost, requestDetails); this.remainingBudget -= cost; this.hbarLimitRemainingGauge.set(this.remainingBudget); // Done asynchronously in the background - this.updateAverageDailyUsagePerSubscriptionType(spendingPlan.subscriptionType).then(); + this.updateAverageDailyUsagePerSubscriptionType(spendingPlan.subscriptionType, requestDetails).then(); this.logger.trace( - `${requestIdPrefix} HBAR rate limit expense update: cost=${cost}, remainingBudget=${this.remainingBudget}`, + `${requestDetails.formattedRequestId} HBAR rate limit expense update: cost=${cost}, remainingBudget=${this.remainingBudget}`, ); } @@ -248,7 +246,7 @@ export class HbarLimitService implements IHbarLimitService { * @param {string} mode - The mode of the transaction or request. * @param {string} methodName - The name of the method being invoked. * @param {number} estimatedTxFee - The total estimated transaction fee, default to 0. - * @param {string} [requestIdPrefix] - An optional prefix to include in log messages. + * @param {RequestDetails} requestDetails The request details for logging and tracking * @returns {Promise} - Resolves `true` if the daily budget has been exceeded, otherwise `false`. * @private */ @@ -256,20 +254,20 @@ export class HbarLimitService implements IHbarLimitService { mode: string, methodName: string, estimatedTxFee: number = 0, - requestIdPrefix?: string, + requestDetails: RequestDetails, ): Promise { if (this.shouldResetLimiter()) { - await this.resetLimiter(); + await this.resetLimiter(requestDetails); } if (this.remainingBudget <= 0 || this.remainingBudget - estimatedTxFee < 0) { this.hbarLimitCounter.labels(mode, methodName).inc(1); this.logger.warn( - `${requestIdPrefix} HBAR rate limit incoming call: remainingBudget=${this.remainingBudget}, totalBudget=${this.totalBudget}, estimatedTxFee=${estimatedTxFee}, resetTimestamp=${this.reset}`, + `${requestDetails.formattedRequestId} HBAR rate limit incoming call: remainingBudget=${this.remainingBudget}, totalBudget=${this.totalBudget}, resetTimestamp=${this.reset}`, ); return true; } else { this.logger.trace( - `${requestIdPrefix} HBAR rate limit not reached: remainingBudget=${this.remainingBudget}, totalBudget=${this.totalBudget}, estimatedTxFee=${estimatedTxFee} resetTimestamp=${this.reset}.`, + `${requestDetails.formattedRequestId} HBAR rate limit not reached. ${this.remainingBudget} out of ${this.totalBudget} tℏ left in relay budget until ${this.reset}.`, ); return false; } @@ -278,10 +276,17 @@ export class HbarLimitService implements IHbarLimitService { /** * Updates the average daily usage per subscription type. * @param {SubscriptionType} subscriptionType - The subscription type to update the average daily usage for. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @private {Promise} - A promise that resolves when the average daily usage has been updated. */ - private async updateAverageDailyUsagePerSubscriptionType(subscriptionType: SubscriptionType): Promise { - const plans = await this.hbarSpendingPlanRepository.findAllActiveBySubscriptionType(subscriptionType); + private async updateAverageDailyUsagePerSubscriptionType( + subscriptionType: SubscriptionType, + requestDetails: RequestDetails, + ): Promise { + const plans = await this.hbarSpendingPlanRepository.findAllActiveBySubscriptionType( + subscriptionType, + requestDetails, + ); const totalUsage = plans.reduce((total, plan) => total + plan.spentToday, 0); const averageUsage = Math.round(totalUsage / plans.length); this.averageDailySpendingPlanUsagesGauge[subscriptionType].set(averageUsage); @@ -329,21 +334,29 @@ export class HbarLimitService implements IHbarLimitService { /** * Gets the spending plan for the given eth address or ip address. * @param {string} ethAddress - The eth address to get the spending plan for. - * @param {string} [ipAddress] - The ip address to get the spending plan for. + * @param {string} ipAddress - The ip address to get the spending plan for. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the spending plan or null if none exists. * @private */ - private async getSpendingPlan(ethAddress: string, ipAddress?: string): Promise { + private async getSpendingPlan( + ethAddress: string, + requestDetails: RequestDetails, + ): Promise { + const ipAddress = requestDetails.ipAddress; if (ethAddress) { try { - return await this.getSpendingPlanByEthAddress(ethAddress); + return await this.getSpendingPlanByEthAddress(ethAddress, requestDetails); } catch (error) { - this.logger.warn(error, `Failed to get spending plan for eth address '${ethAddress}'`); + this.logger.warn( + error, + `${requestDetails.formattedRequestId} Failed to get spending plan for eth address '${ethAddress}'`, + ); } } if (ipAddress) { try { - return await this.getSpendingPlanByIPAddress(ipAddress); + return await this.getSpendingPlanByIPAddress(requestDetails); } catch (error) { this.logger.warn(error, `Failed to get spending plan for IP address '${ipAddress}'`); } @@ -354,12 +367,19 @@ export class HbarLimitService implements IHbarLimitService { /** * Gets the spending plan for the given eth address. * @param {string} ethAddress - The eth address to get the spending plan for. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the spending plan. * @private */ - private async getSpendingPlanByEthAddress(ethAddress: string): Promise { - const ethAddressHbarSpendingPlan = await this.ethAddressHbarSpendingPlanRepository.findByAddress(ethAddress); - return this.hbarSpendingPlanRepository.findByIdWithDetails(ethAddressHbarSpendingPlan.planId); + private async getSpendingPlanByEthAddress( + ethAddress: string, + requestDetails: RequestDetails, + ): Promise { + const ethAddressHbarSpendingPlan = await this.ethAddressHbarSpendingPlanRepository.findByAddress( + ethAddress, + requestDetails, + ); + return this.hbarSpendingPlanRepository.findByIdWithDetails(ethAddressHbarSpendingPlan.planId, requestDetails); } /** @@ -368,31 +388,44 @@ export class HbarLimitService implements IHbarLimitService { * @returns {Promise} - A promise that resolves with the spending plan. * @private */ - private async getSpendingPlanByIPAddress(ipAddress: string): Promise { - const ipAddressHbarSpendingPlan = await this.ipAddressHbarSpendingPlanRepository.findByAddress(ipAddress); - return this.hbarSpendingPlanRepository.findByIdWithDetails(ipAddressHbarSpendingPlan.planId); + private async getSpendingPlanByIPAddress(requestDetails: RequestDetails): Promise { + const ipAddress = requestDetails.ipAddress; + const ipAddressHbarSpendingPlan = await this.ipAddressHbarSpendingPlanRepository.findByAddress( + ipAddress, + requestDetails, + ); + return this.hbarSpendingPlanRepository.findByIdWithDetails(ipAddressHbarSpendingPlan.planId, requestDetails); } /** * Creates a basic spending plan for the given eth address. * @param {string} ethAddress - The eth address to create the spending plan for. - * @param {string} [ipAddress] - The ip address to create the spending plan for. + * @param {string} ipAddress - The ip address to create the spending plan for. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves with the created spending plan. * @private */ - private async createBasicSpendingPlan(ethAddress: string, ipAddress?: string): Promise { + private async createBasicSpendingPlan( + ethAddress: string, + requestDetails: RequestDetails, + ): Promise { + const ipAddress = requestDetails.ipAddress; if (!ethAddress && !ipAddress) { throw new Error('Cannot create a spending plan without an associated eth address or ip address'); } - const spendingPlan = await this.hbarSpendingPlanRepository.create(SubscriptionType.BASIC); + const spendingPlan = await this.hbarSpendingPlanRepository.create(SubscriptionType.BASIC, requestDetails); if (ethAddress) { - this.logger.trace(`Linking spending plan with ID ${spendingPlan.id} to eth address ${ethAddress}`); - await this.ethAddressHbarSpendingPlanRepository.save({ ethAddress, planId: spendingPlan.id }); + this.logger.trace( + `${requestDetails.formattedRequestId} Linking spending plan with ID ${spendingPlan.id} to eth address ${ethAddress}`, + ); + await this.ethAddressHbarSpendingPlanRepository.save({ ethAddress, planId: spendingPlan.id }, requestDetails); } if (ipAddress) { - this.logger.trace(`Linking spending plan with ID ${spendingPlan.id} to ip address ${ipAddress}`); - await this.ipAddressHbarSpendingPlanRepository.save({ ipAddress, planId: spendingPlan.id }); + this.logger.trace( + `${requestDetails.formattedRequestId} Linking spending plan with ID ${spendingPlan.id} to ip address ${ipAddress}`, + ); + await this.ipAddressHbarSpendingPlanRepository.save({ ipAddress, planId: spendingPlan.id }, requestDetails); } return spendingPlan; } diff --git a/packages/relay/src/lib/services/metricService/metricService.ts b/packages/relay/src/lib/services/metricService/metricService.ts index cdd8764547..ff99a1c09c 100644 --- a/packages/relay/src/lib/services/metricService/metricService.ts +++ b/packages/relay/src/lib/services/metricService/metricService.ts @@ -24,8 +24,8 @@ import constants from '../../constants'; import HbarLimit from '../../hbarlimiter'; import { Histogram, Registry } from 'prom-client'; import { MirrorNodeClient, SDKClient } from '../../clients'; -import { formatRequestIdMessage } from '../../../formatters'; import { ITransactionRecordMetric, IExecuteQueryEventPayload, IExecuteTransactionEventPayload } from '../../types'; +import { RequestDetails } from '../../types'; export default class MetricService { /** @@ -113,7 +113,7 @@ export default class MetricService { this.consensusNodeClientHistogramGasFee = this.initGasMetric(register); this.eventEmitter.on(constants.EVENTS.EXECUTE_TRANSACTION, (args: IExecuteTransactionEventPayload) => { - this.captureTransactionMetrics(args); + this.captureTransactionMetrics(args).then(); }); this.eventEmitter.on(constants.EVENTS.EXECUTE_QUERY, (args: IExecuteQueryEventPayload) => { @@ -126,28 +126,28 @@ export default class MetricService { * and recording the transaction fees, gas usage, and other relevant metrics. * * @param {IExecuteTransactionEventPayload} payload - The payload object containing transaction details. - * @param {string} payload.requestId - The unique identifier for the request. * @param {string} payload.callerName - The name of the entity calling the transaction. * @param {string} payload.transactionId - The unique identifier for the transaction. * @param {string} payload.txConstructorName - The name of the transaction constructor. * @param {string} payload.operatorAccountId - The account ID of the operator managing the transaction. * @param {string} payload.interactingEntity - The entity interacting with the transaction. + * @param {RequestDetails} payload.requestDetails - The request details for logging and tracking. * @returns {Promise} - A promise that resolves when the transaction metrics have been captured. */ public async captureTransactionMetrics({ - requestId, callerName, transactionId, txConstructorName, operatorAccountId, interactingEntity, + requestDetails, }: IExecuteTransactionEventPayload): Promise { const transactionRecordMetrics = await this.getTransactionRecordMetrics( transactionId, callerName, - requestId, txConstructorName, operatorAccountId, + requestDetails, ); if (transactionRecordMetrics) { @@ -162,7 +162,7 @@ export default class MetricService { gasUsed, interactingEntity, status, - requestId, + requestDetails, } as IExecuteQueryEventPayload); } @@ -176,7 +176,7 @@ export default class MetricService { gasUsed: 0, interactingEntity, status, - requestId, + requestDetails, } as IExecuteQueryEventPayload); } } @@ -194,7 +194,7 @@ export default class MetricService { * @param {number} payload.gasUsed - The amount of gas used during the transaction. * @param {string} payload.interactingEntity - The entity interacting with the transaction. * @param {string} payload.status - The entity interacting with the transaction. - * @param {string} payload.requestId - The unique identifier for the request. + * @param {string} payload.requestDetails - The request details for logging and tracking. * @returns {void} - This method does not return a value. */ public addExpenseAndCaptureMetrics = ({ @@ -206,35 +206,16 @@ export default class MetricService { gasUsed, interactingEntity, status, - requestId, + requestDetails, }: IExecuteQueryEventPayload): void => { - const formattedRequestId = formatRequestIdMessage(requestId); this.logger.trace( - `${formattedRequestId} Capturing HBAR charged: executionMode=${executionMode} transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}, cost=${cost} tinybars`, + `${requestDetails.formattedRequestId} Capturing HBAR charged: executionMode=${executionMode} transactionId=${transactionId}, txConstructorName=${txConstructorName}, callerName=${callerName}, cost=${cost} tinybars`, ); - this.hbarLimiter.addExpense(cost, Date.now(), requestId); + this.hbarLimiter.addExpense(cost, Date.now(), requestDetails); this.captureMetrics(executionMode, txConstructorName, status, cost, gasUsed, callerName, interactingEntity); }; - /** - * Retrieves the cost metric for consensus node client operations. - * - * @returns {Histogram} - The histogram metric tracking the cost of consensus node client operations. - */ - private getCostMetric(): Histogram { - return this.consensusNodeClientHistogramCost; - } - - /** - * Retrieves the gas fee metric for consensus node client operations. - * - * @returns {Histogram} - The histogram metric tracking the gas fees of consensus node client operations. - */ - private getGasFeeMetric(): Histogram { - return this.consensusNodeClientHistogramGasFee; - } - /** * Initialize consensus node cost metrics * @param {Registry} register @@ -299,19 +280,18 @@ export default class MetricService { * * @param {string} transactionId - The ID of the transaction for which metrics are being retrieved. * @param {string} callerName - The name of the caller requesting the metrics. - * @param {string} requestId - The request ID for tracing the request flow. * @param {string} txConstructorName - The name of the transaction constructor. * @param {string} operatorAccountId - The account ID of the operator. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} - The transaction record metrics or undefined if retrieval fails. */ private async getTransactionRecordMetrics( transactionId: string, callerName: string, - requestId: string, txConstructorName: string, operatorAccountId: string, + requestDetails: RequestDetails, ): Promise { - const formattedRequestId = formatRequestIdMessage(requestId); const defaultToConsensusNode = process.env.GET_RECORD_DEFAULT_TO_CONSENSUS_NODE === 'true'; // retrieve transaction metrics @@ -320,21 +300,24 @@ export default class MetricService { return await this.sdkClient.getTransactionRecordMetrics( transactionId, callerName, - requestId, txConstructorName, operatorAccountId, + requestDetails, ); } else { return await this.mirrorNodeClient.getTransactionRecordMetrics( transactionId, callerName, - requestId, txConstructorName, operatorAccountId, + requestDetails, ); } } catch (error: any) { - this.logger.warn(error, `${formattedRequestId} Could not fetch transaction record: error=${error.message}`); + this.logger.warn( + error, + `${requestDetails.formattedRequestId} Could not fetch transaction record: error=${error.message}`, + ); } } } diff --git a/packages/relay/src/lib/types/RequestDetails.ts b/packages/relay/src/lib/types/RequestDetails.ts new file mode 100644 index 0000000000..81899932f6 --- /dev/null +++ b/packages/relay/src/lib/types/RequestDetails.ts @@ -0,0 +1,33 @@ +/* + * + * Hedera JSON RPC Relay + * + * Copyright (C) 2022-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 class RequestDetails { + requestId: string; + ipAddress: string; + + constructor(details: { requestId: string; ipAddress: string }) { + this.requestId = details.requestId; + this.ipAddress = details.ipAddress; + } + + get formattedRequestId(): string { + return `[Request ID: ${this.requestId}]`; + } +} diff --git a/packages/relay/src/lib/types/events.ts b/packages/relay/src/lib/types/events.ts index 96987ea912..cdf110552b 100644 --- a/packages/relay/src/lib/types/events.ts +++ b/packages/relay/src/lib/types/events.ts @@ -18,13 +18,15 @@ * */ +import { RequestDetails } from './RequestDetails'; + export interface IExecuteTransactionEventPayload { transactionId: string; callerName: string; - requestId: string; txConstructorName: string; operatorAccountId: string; interactingEntity: string; + requestDetails: RequestDetails; } export interface IExecuteQueryEventPayload { @@ -36,5 +38,5 @@ export interface IExecuteQueryEventPayload { gasUsed: number; interactingEntity: string; status: string; - requestId: string; + requestDetails: RequestDetails; } diff --git a/packages/relay/src/lib/types/index.ts b/packages/relay/src/lib/types/index.ts index 267439a689..cf86e73deb 100644 --- a/packages/relay/src/lib/types/index.ts +++ b/packages/relay/src/lib/types/index.ts @@ -38,6 +38,7 @@ import { MirrorNodeTransactionRecord, IMirrorNodeTransactionRecord, } from './mirrorNode'; +import { RequestDetails } from './RequestDetails'; export { ITransfer, @@ -61,4 +62,5 @@ export { MirrorNodeTransactionRecord, IMirrorNodeTransactionRecord, IExecuteTransactionEventPayload, + RequestDetails, }; diff --git a/packages/relay/src/utils.ts b/packages/relay/src/utils.ts index 463dd3793a..369b347d70 100644 --- a/packages/relay/src/utils.ts +++ b/packages/relay/src/utils.ts @@ -20,6 +20,7 @@ import { PrivateKey } from '@hashgraph/sdk'; import constants from './lib/constants'; +import crypto from 'crypto'; export class Utils { public static readonly addPercentageBufferToGasPrice = (gasPrice: number): number => { @@ -55,4 +56,13 @@ export class Utils { throw new Error(`Invalid OPERATOR_KEY_FORMAT provided: ${process.env.OPERATOR_KEY_FORMAT}`); } } + + /** + * Generates a random trace ID for requests. + * + * @returns {string} The generated random trace ID. + */ + public static generateRequestId = (): string => { + return crypto.randomUUID(); + }; } diff --git a/packages/relay/tests/helpers.ts b/packages/relay/tests/helpers.ts index 7aa3d6ec23..cedce4c6e0 100644 --- a/packages/relay/tests/helpers.ts +++ b/packages/relay/tests/helpers.ts @@ -73,10 +73,10 @@ const getRequestId = () => { return formatRequestIdMessage(uuid()); }; -export const ethCallFailing = async (ethImpl, args, block, assertFunc) => { +export const ethCallFailing = async (ethImpl, args, block, requestDetails, assertFunc) => { let hasError = false; try { - await ethImpl.call(args, block); + await ethImpl.call(args, block, requestDetails); } catch (error: any) { hasError = true; assertFunc(error); diff --git a/packages/relay/tests/lib/clients/localLRUCache.spec.ts b/packages/relay/tests/lib/clients/localLRUCache.spec.ts index 446f9dcafb..f2a27284d0 100644 --- a/packages/relay/tests/lib/clients/localLRUCache.spec.ts +++ b/packages/relay/tests/lib/clients/localLRUCache.spec.ts @@ -24,6 +24,7 @@ import { Registry } from 'prom-client'; import pino from 'pino'; import { LocalLRUCache } from '../../../src/lib/clients'; import constants from '../../../src/lib/constants'; +import { RequestDetails } from '../../../src/lib/types'; const logger = pino(); const registry = new Registry(); @@ -36,6 +37,8 @@ chai.use(chaiAsPromised); describe('LocalLRUCache Test Suite', async function () { this.timeout(10000); + const requestDetails = new RequestDetails({ requestId: 'localLRUCacheTest', ipAddress: '0.0.0.0' }); + this.beforeAll(() => { localLRUCache = new LocalLRUCache(logger.child({ name: `cache` }), registry); }); @@ -46,65 +49,65 @@ describe('LocalLRUCache Test Suite', async function () { describe('verify simple cache', async function () { it('get on empty cache return null', async function () { - const cacheValue = await localLRUCache.get('test', callingMethod); + const cacheValue = await localLRUCache.get('test', callingMethod, requestDetails); expect(cacheValue).to.be.null; }); it('get on valid string cache returns non null', async function () { const key = 'key'; const expectedValue = 'value'; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValue = await localLRUCache.get(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValue = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.equal(expectedValue); }); it('get on valid int cache returns non null', async function () { const key = 'key'; const expectedValue = 1; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValue = await localLRUCache.get(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValue = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.equal(expectedValue); }); it('get on valid false boolean cache returns non null', async function () { const key = 'key'; const expectedValue = false; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValue = await localLRUCache.get(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValue = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.equal(expectedValue); }); it('get on valid true boolean cache returns non null', async function () { const key = 'key'; const expectedValue = true; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValue = await localLRUCache.get(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValue = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.equal(expectedValue); }); it('get on valid object cache returns non null', async function () { const key = 'key'; const expectedValue = { key: 'value' }; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValue = await localLRUCache.get(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValue = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.equal(expectedValue); }); it('delete a valid object', async function () { const key = 'key'; const expectedValue = { key: 'value' }; - await localLRUCache.set(key, expectedValue, callingMethod); - const cacheValueBeforeDelete = await localLRUCache.get(key, callingMethod); - localLRUCache.delete(key, callingMethod); + await localLRUCache.set(key, expectedValue, callingMethod, requestDetails); + const cacheValueBeforeDelete = await localLRUCache.get(key, callingMethod, requestDetails); + await localLRUCache.delete(key, callingMethod, requestDetails); - const cacheValueAfterDelete = await localLRUCache.get(key, callingMethod); + const cacheValueAfterDelete = await localLRUCache.get(key, callingMethod, requestDetails); expect(cacheValueBeforeDelete).to.not.be.null; expect(cacheValueAfterDelete).to.be.null; }); }); describe('verify cache management', async function () { - this.beforeEach(() => { + beforeEach(() => { process.env.CACHE_MAX = constants.CACHE_MAX.toString(); }); @@ -119,12 +122,12 @@ describe('LocalLRUCache Test Suite', async function () { }; Object.entries(keyValuePairs).forEach(([key, value]) => { - customLocalLRUCache.set(key, value, callingMethod); + customLocalLRUCache.set(key, value, callingMethod, requestDetails); }); - const key1 = await customLocalLRUCache.get('key1', callingMethod); - const key2 = await customLocalLRUCache.get('key2', callingMethod); - const key3 = await customLocalLRUCache.get('key3', callingMethod); + const key1 = await customLocalLRUCache.get('key1', callingMethod, requestDetails); + const key2 = await customLocalLRUCache.get('key2', callingMethod, requestDetails); + const key3 = await customLocalLRUCache.get('key3', callingMethod, requestDetails); // expect cache to have capped at max size expect(key1).to.be.null; // key1 should have been evicted expect(key2).to.be.equal(keyValuePairs.key2); @@ -135,20 +138,20 @@ describe('LocalLRUCache Test Suite', async function () { const customLocalLRUCache = new LocalLRUCache(logger.child({ name: `cache` }), registry); const key = 'key'; let valueCount = 0; // keep track of values sets - await customLocalLRUCache.set(key, ++valueCount, callingMethod); - await customLocalLRUCache.set(key, ++valueCount, callingMethod); - await customLocalLRUCache.set(key, ++valueCount, callingMethod); - const cacheValue = await customLocalLRUCache.get(key, callingMethod); - // expect cache to have latest value for key + await customLocalLRUCache.set(key, ++valueCount, callingMethod, requestDetails); + await customLocalLRUCache.set(key, ++valueCount, callingMethod, requestDetails); + await customLocalLRUCache.set(key, ++valueCount, callingMethod, requestDetails); + const cacheValue = await customLocalLRUCache.get(key, callingMethod, requestDetails); + // expect cache to have the latest value for key expect(cacheValue).to.be.equal(valueCount); }); it('verify cache ttl nature', async function () { const customLocalLRUCache = new LocalLRUCache(logger.child({ name: `cache` }), registry); const key = 'key'; - await customLocalLRUCache.set(key, 'value', callingMethod, 100); // set ttl to 1 ms + await customLocalLRUCache.set(key, 'value', callingMethod, requestDetails, 100); // set ttl to 1 ms await new Promise((r) => setTimeout(r, 500)); // wait for ttl to expire - const cacheValue = await customLocalLRUCache.get(key, callingMethod); + const cacheValue = await customLocalLRUCache.get(key, callingMethod, requestDetails); expect(cacheValue).to.be.null; }); }); @@ -157,17 +160,17 @@ describe('LocalLRUCache Test Suite', async function () { it('should retrieve keys matching a glob-style pattern with *', async function () { const keys = ['hello', 'hallo', 'hxllo']; for (let i = 0; i < keys.length; i++) { - await localLRUCache.set(keys[i], `value${i}`, callingMethod); + await localLRUCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } - await expect(localLRUCache.keys('h*llo', callingMethod)).to.eventually.have.members(keys); + await expect(localLRUCache.keys('h*llo', callingMethod, requestDetails)).to.eventually.have.members(keys); }); it('should retrieve keys matching a glob-style pattern with ?', async function () { const keys = ['hello', 'hallo', 'hxllo']; for (let i = 0; i < keys.length; i++) { - await localLRUCache.set(keys[i], `value${i}`, callingMethod); + await localLRUCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } - await expect(localLRUCache.keys('h?llo', callingMethod)).to.eventually.have.members(keys); + await expect(localLRUCache.keys('h?llo', callingMethod, requestDetails)).to.eventually.have.members(keys); }); it('should retrieve keys matching a glob-style pattern with []', async function () { @@ -175,10 +178,10 @@ describe('LocalLRUCache Test Suite', async function () { const key2 = 'hallo'; const pattern = 'h[ae]llo'; - await localLRUCache.set(key1, 'value1', callingMethod); - await localLRUCache.set(key2, 'value2', callingMethod); + await localLRUCache.set(key1, 'value1', callingMethod, requestDetails); + await localLRUCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await localLRUCache.keys(pattern, callingMethod); + const keys = await localLRUCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); @@ -187,10 +190,10 @@ describe('LocalLRUCache Test Suite', async function () { const key2 = 'hbllo'; const pattern = 'h[^e]llo'; - await localLRUCache.set(key1, 'value1', callingMethod); - await localLRUCache.set(key2, 'value2', callingMethod); + await localLRUCache.set(key1, 'value1', callingMethod, requestDetails); + await localLRUCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await localLRUCache.keys(pattern, callingMethod); + const keys = await localLRUCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); @@ -199,22 +202,22 @@ describe('LocalLRUCache Test Suite', async function () { const key2 = 'hbllo'; const pattern = 'h[a-b]llo'; - await localLRUCache.set(key1, 'value1', callingMethod); - await localLRUCache.set(key2, 'value2', callingMethod); + await localLRUCache.set(key1, 'value1', callingMethod, requestDetails); + await localLRUCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await localLRUCache.keys(pattern, callingMethod); + const keys = await localLRUCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); it('should retrieve keys matching a pattern with escaped special characters', async function () { const keys = ['h*llo', 'h?llo', 'h[llo', 'h]llo']; for (let i = 0; i < keys.length; i++) { - await localLRUCache.set(keys[i], `value${i}`, callingMethod); + await localLRUCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } for (const key of keys) { - await expect(localLRUCache.keys(key.replace(/([*?[\]])/g, '\\$1'), callingMethod)).eventually.has.members([ - key, - ]); + await expect( + localLRUCache.keys(key.replace(/([*?[\]])/g, '\\$1'), callingMethod, requestDetails), + ).eventually.has.members([key]); } }); @@ -224,11 +227,11 @@ describe('LocalLRUCache Test Suite', async function () { const key3 = 'age'; const pattern = '*'; - await localLRUCache.set(key1, 'Jack', callingMethod); - await localLRUCache.set(key2, 'Stuntman', callingMethod); - await localLRUCache.set(key3, '35', callingMethod); + await localLRUCache.set(key1, 'Jack', callingMethod, requestDetails); + await localLRUCache.set(key2, 'Stuntman', callingMethod, requestDetails); + await localLRUCache.set(key3, '35', callingMethod, requestDetails); - const keys = await localLRUCache.keys(pattern, callingMethod); + const keys = await localLRUCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2, key3]); }); }); diff --git a/packages/relay/tests/lib/clients/redisCache.spec.ts b/packages/relay/tests/lib/clients/redisCache.spec.ts index 89d5a6a485..6c6b966a34 100644 --- a/packages/relay/tests/lib/clients/redisCache.spec.ts +++ b/packages/relay/tests/lib/clients/redisCache.spec.ts @@ -24,6 +24,7 @@ import chaiAsPromised from 'chai-as-promised'; import { RedisCache } from '../../../src/lib/clients'; import { Registry } from 'prom-client'; import { useInMemoryRedisServer } from '../../helpers'; +import { RequestDetails } from '../../../dist/lib/types'; chai.use(chaiAsPromised); @@ -33,6 +34,8 @@ describe('RedisCache Test Suite', async function () { const logger = pino(); const registry = new Registry(); const callingMethod = 'RedisCacheTest'; + const requestDetails = new RequestDetails({ requestId: 'localLRUCacheTest', ipAddress: '0.0.0.0' }); + let redisCache: RedisCache; @@ -57,7 +60,7 @@ describe('RedisCache Test Suite', async function () { describe('Get and Set Test Suite', async function () { it('should get null on empty cache', async function () { - const cacheValue = await redisCache.get('test', callingMethod); + const cacheValue = await redisCache.get('test', callingMethod, requestDetails); expect(cacheValue).to.be.null; }); @@ -65,9 +68,9 @@ describe('RedisCache Test Suite', async function () { const key = 'int'; const value = 1; - await redisCache.set(key, value, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).equal(value); }); @@ -75,9 +78,9 @@ describe('RedisCache Test Suite', async function () { const key = 'boolean'; const value = false; - await redisCache.set(key, value, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).equal(value); }); @@ -85,9 +88,9 @@ describe('RedisCache Test Suite', async function () { const key = 'array'; const value = ['false']; - await redisCache.set(key, value, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).deep.equal(value); }); @@ -95,9 +98,9 @@ describe('RedisCache Test Suite', async function () { const key = 'object'; const value = { result: true }; - await redisCache.set(key, value, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).deep.equal(value); }); @@ -106,14 +109,14 @@ describe('RedisCache Test Suite', async function () { const value = 1; const ttl = 500; - await redisCache.set(key, value, callingMethod, ttl); + await redisCache.set(key, value, callingMethod, requestDetails, ttl); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).equal(value); await new Promise((resolve) => setTimeout(resolve, ttl + 100)); - const expiredValue = await redisCache.get(key, callingMethod); + const expiredValue = await redisCache.get(key, callingMethod, requestDetails); expect(expiredValue).to.be.null; }); @@ -122,14 +125,14 @@ describe('RedisCache Test Suite', async function () { const value = 1; const ttl = 1500; - await redisCache.set(key, value, callingMethod, ttl); + await redisCache.set(key, value, callingMethod, requestDetails, ttl); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).equal(value); await new Promise((resolve) => setTimeout(resolve, ttl + 100)); - const expiredValue = await redisCache.get(key, callingMethod); + const expiredValue = await redisCache.get(key, callingMethod, requestDetails); expect(expiredValue).to.be.null; }); }); @@ -144,10 +147,10 @@ describe('RedisCache Test Suite', async function () { object: { result: true }, }; - await redisCache.multiSet(keyValuePairs, callingMethod); + await redisCache.multiSet(keyValuePairs, callingMethod, requestDetails); for (const key in keyValuePairs) { - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).deep.equal(keyValuePairs[key]); } }); @@ -163,10 +166,10 @@ describe('RedisCache Test Suite', async function () { object: { result: true }, }; - await redisCache.pipelineSet(keyValuePairs, callingMethod); + await redisCache.pipelineSet(keyValuePairs, callingMethod, requestDetails); for (const key in keyValuePairs) { - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).deep.equal(keyValuePairs[key]); } }); @@ -180,17 +183,17 @@ describe('RedisCache Test Suite', async function () { object: { result: true }, }; - await redisCache.pipelineSet(keyValuePairs, callingMethod, 500); + await redisCache.pipelineSet(keyValuePairs, callingMethod, requestDetails, 500); for (const key in keyValuePairs) { - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).deep.equal(keyValuePairs[key]); } await new Promise((resolve) => setTimeout(resolve, 500)); for (const key in keyValuePairs) { - const expiredValue = await redisCache.get(key, callingMethod); + const expiredValue = await redisCache.get(key, callingMethod, requestDetails); expect(expiredValue).to.be.null; } }); @@ -201,10 +204,10 @@ describe('RedisCache Test Suite', async function () { const key = 'int'; const value = 1; - await redisCache.set(key, value, callingMethod); - await redisCache.delete(key, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); + await redisCache.delete(key, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); @@ -212,10 +215,10 @@ describe('RedisCache Test Suite', async function () { const key = 'boolean'; const value = false; - await redisCache.set(key, value, callingMethod); - await redisCache.delete(key, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); + await redisCache.delete(key, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); @@ -223,10 +226,10 @@ describe('RedisCache Test Suite', async function () { const key = 'array'; const value = ['false']; - await redisCache.set(key, value, callingMethod); - await redisCache.delete(key, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); + await redisCache.delete(key, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); @@ -234,10 +237,10 @@ describe('RedisCache Test Suite', async function () { const key = 'object'; const value = { result: true }; - await redisCache.set(key, value, callingMethod); - await redisCache.delete(key, callingMethod); + await redisCache.set(key, value, callingMethod, requestDetails); + await redisCache.delete(key, callingMethod, requestDetails); - const cachedValue = await redisCache.get(key, callingMethod); + const cachedValue = await redisCache.get(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); }); @@ -247,7 +250,7 @@ describe('RedisCache Test Suite', async function () { const key = 'non-existing'; const amount = 1; - const newValue = await redisCache.incrBy(key, amount, callingMethod); + const newValue = await redisCache.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).equal(amount); }); @@ -256,8 +259,8 @@ describe('RedisCache Test Suite', async function () { const initialValue = 5; const amount = 3; - await redisCache.set(key, initialValue, callingMethod); - const newValue = await redisCache.incrBy(key, amount, callingMethod); + await redisCache.set(key, initialValue, callingMethod, requestDetails); + const newValue = await redisCache.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).equal(initialValue + amount); }); @@ -266,8 +269,8 @@ describe('RedisCache Test Suite', async function () { const initialValue = 5; const amount = -2; - await redisCache.set(key, initialValue, callingMethod); - const newValue = await redisCache.incrBy(key, amount, callingMethod); + await redisCache.set(key, initialValue, callingMethod, requestDetails); + const newValue = await redisCache.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).equal(initialValue + amount); }); }); @@ -277,10 +280,10 @@ describe('RedisCache Test Suite', async function () { const key = 'non-existing-list'; const value = 'item1'; - const length = await redisCache.rPush(key, value, callingMethod); + const length = await redisCache.rPush(key, value, callingMethod, requestDetails); expect(length).equal(1); - const list = await redisCache.lRange(key, 0, -1, callingMethod); + const list = await redisCache.lRange(key, 0, -1, callingMethod, requestDetails); expect(list).deep.equal([value]); }); @@ -289,11 +292,11 @@ describe('RedisCache Test Suite', async function () { const initialList = ['item1']; const newValue = 'item2'; - await redisCache.rPush(key, initialList[0], callingMethod); - const length = await redisCache.rPush(key, newValue, callingMethod); + await redisCache.rPush(key, initialList[0], callingMethod, requestDetails); + const length = await redisCache.rPush(key, newValue, callingMethod, requestDetails); expect(length).equal(2); - const list = await redisCache.lRange(key, 0, -1, callingMethod); + const list = await redisCache.lRange(key, 0, -1, callingMethod, requestDetails); expect(list).deep.equal([...initialList, newValue]); }); }); @@ -304,7 +307,7 @@ describe('RedisCache Test Suite', async function () { const start = 0; const end = 1; - const list = await redisCache.lRange(key, start, end, callingMethod); + const list = await redisCache.lRange(key, start, end, callingMethod, requestDetails); expect(list).deep.equal([]); }); @@ -313,10 +316,10 @@ describe('RedisCache Test Suite', async function () { const list = ['item1', 'item2', 'item3']; for (const item of list) { - await redisCache.rPush(key, item, callingMethod); + await redisCache.rPush(key, item, callingMethod, requestDetails); } - const range = await redisCache.lRange(key, 0, 1, callingMethod); + const range = await redisCache.lRange(key, 0, 1, callingMethod, requestDetails); expect(range).deep.equal(['item1', 'item2']); }); }); @@ -325,17 +328,17 @@ describe('RedisCache Test Suite', async function () { it('should retrieve keys matching a glob-style pattern with *', async function () { const keys = ['hello', 'hallo', 'hxllo']; for (let i = 0; i < keys.length; i++) { - await redisCache.set(keys[i], `value${i}`, callingMethod); + await redisCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } - await expect(redisCache.keys('h*llo', callingMethod)).to.eventually.have.members(keys); + await expect(redisCache.keys('h*llo', callingMethod, requestDetails)).to.eventually.have.members(keys); }); it('should retrieve keys matching a glob-style pattern with ?', async function () { const keys = ['hello', 'hallo', 'hxllo']; for (let i = 0; i < keys.length; i++) { - await redisCache.set(keys[i], `value${i}`, callingMethod); + await redisCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } - await expect(redisCache.keys('h?llo', callingMethod)).to.eventually.have.members(keys); + await expect(redisCache.keys('h?llo', callingMethod, requestDetails)).to.eventually.have.members(keys); }); it('should retrieve keys matching a glob-style pattern with []', async function () { @@ -343,10 +346,10 @@ describe('RedisCache Test Suite', async function () { const key2 = 'hallo'; const pattern = 'h[ae]llo'; - await redisCache.set(key1, 'value1', callingMethod); - await redisCache.set(key2, 'value2', callingMethod); + await redisCache.set(key1, 'value1', callingMethod, requestDetails); + await redisCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await redisCache.keys(pattern, callingMethod); + const keys = await redisCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); @@ -355,10 +358,10 @@ describe('RedisCache Test Suite', async function () { const key2 = 'hbllo'; const pattern = 'h[^e]llo'; - await redisCache.set(key1, 'value1', callingMethod); - await redisCache.set(key2, 'value2', callingMethod); + await redisCache.set(key1, 'value1', callingMethod, requestDetails); + await redisCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await redisCache.keys(pattern, callingMethod); + const keys = await redisCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); @@ -367,20 +370,22 @@ describe('RedisCache Test Suite', async function () { const key2 = 'hbllo'; const pattern = 'h[a-b]llo'; - await redisCache.set(key1, 'value1', callingMethod); - await redisCache.set(key2, 'value2', callingMethod); + await redisCache.set(key1, 'value1', callingMethod, requestDetails); + await redisCache.set(key2, 'value2', callingMethod, requestDetails); - const keys = await redisCache.keys(pattern, callingMethod); + const keys = await redisCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2]); }); it('should retrieve keys matching a pattern with escaped special characters', async function () { const keys = ['h*llo', 'h?llo', 'h[llo', 'h]llo']; for (let i = 0; i < keys.length; i++) { - await redisCache.set(keys[i], `value${i}`, callingMethod); + await redisCache.set(keys[i], `value${i}`, callingMethod, requestDetails); } for (const key of keys) { - await expect(redisCache.keys(key.replace(/([*?[\]])/g, '\\$1'), callingMethod)).eventually.has.members([key]); + await expect( + redisCache.keys(key.replace(/([*?[\]])/g, '\\$1'), callingMethod, requestDetails), + ).eventually.has.members([key]); } }); @@ -390,11 +395,11 @@ describe('RedisCache Test Suite', async function () { const key3 = 'age'; const pattern = '*'; - await redisCache.set(key1, 'Jack', callingMethod); - await redisCache.set(key2, 'Stuntman', callingMethod); - await redisCache.set(key3, '35', callingMethod); + await redisCache.set(key1, 'Jack', callingMethod, requestDetails); + await redisCache.set(key2, 'Stuntman', callingMethod, requestDetails); + await redisCache.set(key3, '35', callingMethod, requestDetails); - const keys = await redisCache.keys(pattern, callingMethod); + const keys = await redisCache.keys(pattern, callingMethod, requestDetails); expect(keys).to.include.members([key1, key2, key3]); }); }); diff --git a/packages/relay/tests/lib/eth/eth_call.spec.ts b/packages/relay/tests/lib/eth/eth_call.spec.ts index 9381d21c13..8b3dd2860a 100644 --- a/packages/relay/tests/lib/eth/eth_call.spec.ts +++ b/packages/relay/tests/lib/eth/eth_call.spec.ts @@ -43,7 +43,7 @@ import { ONE_TINYBAR_IN_WEI_HEX, EXAMPLE_CONTRACT_BYTECODE, } from './eth-config'; -import { JsonRpcError, predefined } from '../../../src/lib/errors/JsonRpcError'; +import { JsonRpcError, predefined } from '../../../src'; import RelayAssertions from '../../assertions'; import constants from '../../../src/lib/constants'; import { @@ -55,13 +55,14 @@ import { mockData, } from '../../helpers'; import { generateEthTestEnv } from './eth-helpers'; -import { IContractCallRequest, IContractCallResponse } from '../../../src/lib/types/IMirrorNode'; +import { IContractCallRequest, IContractCallResponse, RequestDetails } from '../../../src/lib/types'; +import { ContractFunctionResult } from '@hashgraph/sdk'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethCall Eth Call spec', async function () { @@ -75,11 +76,12 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }; - this.beforeEach(() => { + const requestDetails = new RequestDetails({ requestId: 'eth_callTest', ipAddress: '0.0.0.0' }); + + this.beforeEach(async () => { // reset cache and restMock - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -130,7 +132,8 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', - (error) => { + requestDetails, + (error: any) => { expect(error.message).to.equal( `Invalid Contract Address: ${EthImpl.zeroHex}. Expected length of 42 chars but was 3.`, ); @@ -149,7 +152,11 @@ describe('@ethCall Eth Call spec', async function () { evm_address: defaultCallData.from, }); restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, DEFAULT_CONTRACT); - await ethImpl.call({ ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, 'latest'); + await ethImpl.call( + { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, + 'latest', + requestDetails, + ); assert(callMirrorNodeSpy.calledOnce); process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallConesneusFF; @@ -166,7 +173,11 @@ describe('@ethCall Eth Call spec', async function () { evm_address: defaultCallData.from, }); restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, DEFAULT_CONTRACT); - await ethImpl.call({ ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, 'latest'); + await ethImpl.call( + { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, + 'latest', + requestDetails, + ); assert(callMirrorNodeSpy.calledOnce); process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallConesneusFF; @@ -182,7 +193,11 @@ describe('@ethCall Eth Call spec', async function () { evm_address: defaultCallData.from, }); restMock.onGet(`contracts/${defaultCallData.to}`).reply(200, DEFAULT_CONTRACT); - await ethImpl.call({ ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, 'latest'); + await ethImpl.call( + { ...defaultCallData, gas: `0x${defaultCallData.gas.toString(16)}` }, + 'latest', + requestDetails, + ); assert(callConsensusNodeSpy.calledOnce); process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallConesneusFF; @@ -203,6 +218,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', + requestDetails, ), ).to.eventually.be.fulfilled.and.equal('0x1'); }); @@ -223,32 +239,33 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', + requestDetails, ), ).to.eventually.be.fulfilled.and.equal('0x1'); }); }); describe('eth_call using consensus node', async function () { - let initialEthCallConesneusFF; + let initialEthCallDefaultsToConsensus: string | undefined; before(() => { - initialEthCallConesneusFF = process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE; + initialEthCallDefaultsToConsensus = process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE; process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = 'true'; }); after(() => { - process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallConesneusFF; + process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallDefaultsToConsensus; }); it('eth_call with no gas', async function () { restMock.onGet(`contracts/${ACCOUNT_ADDRESS_1}`).reply(404); restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); const result = await ethImpl.call( { @@ -257,6 +274,7 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, }, 'latest', + requestDetails, ); sinon.assert.calledWith( @@ -272,11 +290,11 @@ describe('@ethCall Eth Call spec', async function () { it('eth_call with no data', async function () { restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); const result = await ethImpl.call( { @@ -285,6 +303,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', + requestDetails, ); sinon.assert.calledWith( @@ -308,13 +327,13 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.equal('0x00'); }); @@ -327,7 +346,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect((result as JsonRpcError).code).to.equal(-32014); expect((result as JsonRpcError).message).to.equal( @@ -337,13 +356,13 @@ describe('@ethCall Eth Call spec', async function () { it('eth_call with all fields', async function () { restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); - const result = await ethImpl.call(ETH_CALL_REQ_ARGS, 'latest'); + const result = await ethImpl.call(ETH_CALL_REQ_ARGS, 'latest', requestDetails); sinon.assert.calledWith( sdkClientStub.submitContractCallQueryWithRetry, @@ -359,11 +378,11 @@ describe('@ethCall Eth Call spec', async function () { //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, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); for (let index = 0; index < 3; index++) { const result = await ethImpl.call( @@ -374,6 +393,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', + requestDetails, ); expect(result).to.equal('0x00'); await new Promise((r) => setTimeout(r, 50)); @@ -383,7 +403,7 @@ describe('@ethCall Eth Call spec', async function () { const expectedError = predefined.INVALID_CONTRACT_ADDRESS(CONTRACT_ADDRESS_2); sdkClientStub.submitContractCallQueryWithRetry.throws(expectedError); - const call: string | JsonRpcError = await ethImpl.call(ETH_CALL_REQ_ARGS, 'latest'); + const call: string | JsonRpcError = await ethImpl.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); @@ -395,7 +415,7 @@ describe('@ethCall Eth Call spec', async function () { predefined.CONTRACT_REVERT(defaultErrorMessageText, defaultErrorMessageHex), ); - const result = await ethImpl.call(ETH_CALL_REQ_ARGS, 'latest'); + const result = await ethImpl.call(ETH_CALL_REQ_ARGS, 'latest', requestDetails); expect(result).to.exist; expect((result as JsonRpcError).code).to.equal(3); @@ -412,6 +432,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT_HEX, }, 'latest', + requestDetails, ]; await RelayAssertions.assertRejection( @@ -426,7 +447,7 @@ describe('@ethCall Eth Call spec', async function () { 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, DEFAULT_CONTRACT_2); - sdkClientStub.submitContractCallQueryWithRetry.returns(undefined); + sdkClientStub.submitContractCallQueryWithRetry.resolves(undefined); const result = await ethImpl.call( { @@ -435,6 +456,7 @@ describe('@ethCall Eth Call spec', async function () { gas: 5_000_000, }, 'latest', + requestDetails, ); expect(result).to.exist; @@ -450,7 +472,7 @@ describe('@ethCall Eth Call spec', async function () { gas: 400000, value: null, }; - let initialEthCallConesneusFF; + let initialEthCallConesneusFF: string | undefined; before(() => { initialEthCallConesneusFF = process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE; @@ -478,7 +500,7 @@ describe('@ethCall Eth Call spec', async function () { restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_3_EMPTY_BYTECODE); web3Mock.onPost(`contracts/call`).replyOnce(200, {}); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.equal('0x'); }); @@ -490,11 +512,11 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails); web3Mock.history.post = []; - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(web3Mock.history.post.length).to.gte(1); expect(web3Mock.history.post[0].data).to.equal(JSON.stringify({ ...callData, estimate: false, block: 'latest' })); @@ -510,9 +532,9 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.equal('0x00'); }); @@ -523,8 +545,8 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.equal('0x00'); }); @@ -536,8 +558,8 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.equal('0x00'); }); @@ -546,10 +568,16 @@ describe('@ethCall Eth Call spec', async function () { ...defaultCallData, gas: 25_000_000, }; - await mockContractCall({ ...callData, gas: constants.MAX_GAS_PER_SEC, block: 'latest' }, false, 200, { - result: '0x00', - }); - const res = await ethImpl.call(callData, 'latest'); + await mockContractCall( + { ...callData, gas: constants.MAX_GAS_PER_SEC, block: 'latest' }, + false, + 200, + { + result: '0x00', + }, + requestDetails, + ); + const res = await ethImpl.call(callData, 'latest', requestDetails); expect(res).to.equal('0x00'); }); @@ -564,11 +592,11 @@ describe('@ethCall Eth Call spec', async function () { block: 'latest', }; - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }); + await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: '0x00' }, requestDetails); restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); // Relay is called with value in Weibars - const result = await ethImpl.call({ ...callData, value: ONE_TINYBAR_IN_WEI_HEX }, 'latest'); + const result = await ethImpl.call({ ...callData, value: ONE_TINYBAR_IN_WEI_HEX }, 'latest', requestDetails); expect(result).to.equal('0x00'); }); @@ -580,8 +608,8 @@ describe('@ethCall Eth Call spec', async function () { data: CONTRACT_CALL_DATA, gas: MAX_GAS_LIMIT, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 429, mockData.tooManyRequests); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 429, mockData.tooManyRequests, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.be.not.null; expect((result as JsonRpcError).code).to.eq(-32605); }); @@ -595,8 +623,8 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.be.not.null; expect((result as JsonRpcError).code).to.eq(3); expect((result as JsonRpcError).message).to.contain(mockData.contractReverted._status.messages[0].message); @@ -612,15 +640,15 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 501, mockData.notSuported); + await mockContractCall({ ...callData, block: 'latest' }, false, 501, mockData.notSuported, requestDetails); - sdkClientStub.submitContractCallQueryWithRetry.returns({ + sdkClientStub.submitContractCallQueryWithRetry.resolves({ asBytes: function () { return Uint8Array.of(0); }, - }); + } as unknown as ContractFunctionResult); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); sinon.assert.calledWith( sdkClientStub.submitContractCallQueryWithRetry, @@ -643,9 +671,9 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.contractReverted, requestDetails); sinon.reset(); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); sinon.assert.notCalled(sdkClientStub.submitContractCallQueryWithRetry); expect(result).to.not.be.null; expect((result as JsonRpcError).code).to.eq(3); @@ -662,19 +690,25 @@ describe('@ethCall Eth Call spec', async function () { }; restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - await mockContractCall({ ...callData, block: 'latest' }, false, 400, { - _status: { - messages: [ - { - message: '', - detail: defaultErrorMessageText, - data: defaultErrorMessageHex, - }, - ], + await mockContractCall( + { ...callData, block: 'latest' }, + false, + 400, + { + _status: { + messages: [ + { + message: '', + detail: defaultErrorMessageText, + data: defaultErrorMessageHex, + }, + ], + }, }, - }); + requestDetails, + ); - const result = await ethImpl.call(callData, 'latest'); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.exist; expect((result as JsonRpcError).code).to.eq(3); @@ -692,6 +726,7 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }, 'latest', + requestDetails, ]; await RelayAssertions.assertRejection( @@ -712,8 +747,8 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.invalidTransaction); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.invalidTransaction, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.be.not.null; expect(result).to.equal('0x'); }); @@ -727,8 +762,8 @@ describe('@ethCall Eth Call spec', async function () { gas: MAX_GAS_LIMIT, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.failInvalid); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall({ ...callData, block: 'latest' }, false, 400, mockData.failInvalid, requestDetails); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.be.not.null; expect(result).to.equal('0x'); }); @@ -740,8 +775,14 @@ describe('@ethCall Eth Call spec', async function () { from: ACCOUNT_ADDRESS_1, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: EXAMPLE_CONTRACT_BYTECODE }); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall( + { ...callData, block: 'latest' }, + false, + 200, + { result: EXAMPLE_CONTRACT_BYTECODE }, + requestDetails, + ); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.eq(EXAMPLE_CONTRACT_BYTECODE); }); @@ -751,8 +792,14 @@ describe('@ethCall Eth Call spec', async function () { from: ACCOUNT_ADDRESS_1, }; - await mockContractCall({ ...callData, block: 'latest' }, false, 200, { result: EXAMPLE_CONTRACT_BYTECODE }); - const result = await ethImpl.call(callData, 'latest'); + await mockContractCall( + { ...callData, block: 'latest' }, + false, + 200, + { result: EXAMPLE_CONTRACT_BYTECODE }, + requestDetails, + ); + const result = await ethImpl.call(callData, 'latest', requestDetails); expect(result).to.eq(EXAMPLE_CONTRACT_BYTECODE); }); @@ -761,9 +808,10 @@ describe('@ethCall Eth Call spec', async function () { estimate: boolean, statusCode: number, result: IContractCallResponse, + requestDetails: RequestDetails, ) { const formattedCallData = { ...callData, estimate }; - await ethImpl.contractCallFormat(formattedCallData); + await ethImpl.contractCallFormat(formattedCallData, requestDetails); return web3Mock.onPost('contracts/call', formattedCallData).reply(statusCode, result); } }); @@ -784,7 +832,7 @@ describe('@ethCall Eth Call spec', async function () { value: '0x2540BE400', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.value).to.equal(1); }); @@ -793,7 +841,7 @@ describe('@ethCall Eth Call spec', async function () { gasPrice: '1000000000', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.gasPrice).to.equal(1000000000); }); @@ -803,7 +851,7 @@ describe('@ethCall Eth Call spec', async function () { gas: '50000', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.gas).to.equal(50000); }); @@ -815,7 +863,7 @@ describe('@ethCall Eth Call spec', async function () { input: inputValue, data: dataValue, }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.data).to.eq(inputValue); expect(transaction.data).to.not.eq(dataValue); expect(transaction.input).to.be.undefined; @@ -826,7 +874,7 @@ describe('@ethCall Eth Call spec', async function () { const transaction = { data: dataValue, }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.data).to.eq(dataValue); }); @@ -835,7 +883,7 @@ describe('@ethCall Eth Call spec', async function () { input: 'input data', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); // @ts-ignore expect(transaction.data).to.equal('input data'); @@ -849,7 +897,7 @@ describe('@ethCall Eth Call spec', async function () { gas: '50000', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.value).to.equal(1); expect(transaction.gasPrice).to.equal(1000000000); @@ -862,9 +910,9 @@ describe('@ethCall Eth Call spec', async function () { gasPrice: undefined, }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); - const expectedGasPrice = await ethImpl.gasPrice(); + const expectedGasPrice = await ethImpl.gasPrice(requestDetails); expect(transaction.gasPrice).to.equal(parseInt(expectedGasPrice)); }); @@ -875,14 +923,15 @@ describe('@ethCall Eth Call spec', async function () { from: undefined, }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.from).to.equal(operatorEvmAddress); }); }); describe('eth_call using consensus node because of redirect by selector', async function () { - let initialEthCallConesneusFF: any, initialEthCallSelectorsAlwaysToConsensus: any; + let initialForceToConsensusBySelector: string | undefined; + let initialEthCallDefaultsToConsensus: string | undefined; const REDIRECTED_SELECTOR = '0x4d8fdd6d'; const NON_REDIRECTED_SELECTOR = '0xaaaaaaaa'; let callConsensusNodeSpy: sinon.SinonSpy; @@ -890,14 +939,15 @@ describe('@ethCall Eth Call spec', async function () { let sandbox: sinon.SinonSandbox; before(() => { - initialEthCallConesneusFF = process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE; - initialEthCallSelectorsAlwaysToConsensus = process.env.ETH_CALL_CONSENSUS_SELECTORS; + initialForceToConsensusBySelector = process.env.ETH_CALL_FORCE_TO_CONSENSUS_BY_SELECTOR; + initialEthCallDefaultsToConsensus = process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE; + process.env.ETH_CALL_FORCE_TO_CONSENSUS_BY_SELECTOR = 'true'; process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = 'false'; }); after(() => { - process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallConesneusFF; - process.env.ETH_CALL_CONSENSUS_SELECTORS = initialEthCallSelectorsAlwaysToConsensus; + process.env.ETH_CALL_FORCE_TO_CONSENSUS_BY_SELECTOR = initialForceToConsensusBySelector; + process.env.ETH_CALL_DEFAULT_TO_CONSENSUS_NODE = initialEthCallDefaultsToConsensus; }); beforeEach(() => { @@ -919,6 +969,7 @@ describe('@ethCall Eth Call spec', async function () { data: REDIRECTED_SELECTOR, }, 'latest', + requestDetails, ); assert(callConsensusNodeSpy.calledOnce); @@ -932,6 +983,7 @@ describe('@ethCall Eth Call spec', async function () { data: NON_REDIRECTED_SELECTOR, }, 'latest', + requestDetails, ); assert(callConsensusNodeSpy.notCalled); diff --git a/packages/relay/tests/lib/eth/eth_common.spec.ts b/packages/relay/tests/lib/eth/eth_common.spec.ts index ee598c0cf1..69050d3091 100644 --- a/packages/relay/tests/lib/eth/eth_common.spec.ts +++ b/packages/relay/tests/lib/eth/eth_common.spec.ts @@ -23,78 +23,78 @@ import { expect, use } from 'chai'; import { Registry } from 'prom-client'; import pino from 'pino'; import chaiAsPromised from 'chai-as-promised'; -import { RelayImpl } from '../../../src/lib/relay'; +import { RelayImpl } from '../../../src'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); describe('@ethCommon', async function () { - let Relay: any; - + let Relay: RelayImpl; this.timeout(10000); + const requestDetails = new RequestDetails({ requestId: 'eth_commonTest', ipAddress: '0.0.0.0' }); + this.beforeAll(() => { - const logger = pino(); - const registry = new Registry(); - Relay = new RelayImpl(logger, registry); + Relay = new RelayImpl(pino(), new Registry()); }); describe('@ethCommon', async function () { it('should execute "eth_chainId"', async function () { - const chainId = Relay.eth().chainId(); + const chainId = Relay.eth().chainId(requestDetails); expect(chainId).to.be.equal('0x' + Number(process.env.CHAIN_ID).toString(16)); }); it('should execute "eth_accounts"', async function () { - const accounts = Relay.eth().accounts(); + const accounts = Relay.eth().accounts(requestDetails); expect(accounts).to.be.an('Array'); expect(accounts.length).to.be.equal(0); }); it('should execute "eth_getUncleByBlockHashAndIndex"', async function () { - const result = await Relay.eth().getUncleByBlockHashAndIndex(); + const result = await Relay.eth().getUncleByBlockHashAndIndex(requestDetails); expect(result).to.be.null; }); it('should execute "eth_getUncleByBlockNumberAndIndex"', async function () { - const result = await Relay.eth().getUncleByBlockNumberAndIndex(); + const result = await Relay.eth().getUncleByBlockNumberAndIndex(requestDetails); expect(result).to.be.null; }); it('should execute "eth_getUncleCountByBlockHash"', async function () { - const result = await Relay.eth().getUncleCountByBlockHash(); + const result = await Relay.eth().getUncleCountByBlockHash(requestDetails); expect(result).to.eq('0x0'); }); it('should execute "eth_getUncleCountByBlockNumber"', async function () { - const result = await Relay.eth().getUncleCountByBlockNumber(); + const result = await Relay.eth().getUncleCountByBlockNumber(requestDetails); expect(result).to.eq('0x0'); }); it('should execute "eth_hashrate"', async function () { - const result = await Relay.eth().hashrate(); + const result = await Relay.eth().hashrate(requestDetails); expect(result).to.eq('0x0'); }); it('should execute "eth_mining"', async function () { - const result = await Relay.eth().mining(); + const result = await Relay.eth().mining(requestDetails); expect(result).to.eq(false); }); it('should execute "eth_submitWork"', async function () { - const result = await Relay.eth().submitWork(); + const result = await Relay.eth().submitWork(requestDetails); expect(result).to.eq(false); }); it('should execute "eth_syncing"', async function () { - const result = await Relay.eth().syncing(); + const result = await Relay.eth().syncing(requestDetails); expect(result).to.eq(false); }); it('should execute "eth_getWork"', async function () { - const result = Relay.eth().getWork(); + const result = Relay.eth().getWork(requestDetails); expect(result).to.have.property('code'); expect(result.code).to.be.equal(-32601); expect(result).to.have.property('message'); @@ -102,7 +102,7 @@ describe('@ethCommon', async function () { }); it('should execute "eth_maxPriorityFeePerGas"', async function () { - const result = await Relay.eth().maxPriorityFeePerGas(); + const result = await Relay.eth().maxPriorityFeePerGas(requestDetails); expect(result).to.eq('0x0'); }); }); diff --git a/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts b/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts index 75bbbf8e85..bc75a4ba14 100644 --- a/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts +++ b/packages/relay/tests/lib/eth/eth_estimateGas.spec.ts @@ -31,7 +31,7 @@ import { Precheck } from '../../../src/lib/precheck'; import { SDKClient } from '../../../src/lib/clients'; import { numberTo0x } from '../../../src/formatters'; import { createStubInstance, SinonStub, SinonStubbedInstance, stub } from 'sinon'; -import { IContractCallRequest, IContractCallResponse } from '../../../src/lib/types'; +import { IContractCallRequest, IContractCallResponse, RequestDetails } from '../../../src/lib/types'; import { ACCOUNT_ADDRESS_1, DEFAULT_NETWORK_FEES, @@ -50,17 +50,20 @@ let currentMaxBlockRange: number; describe('@ethEstimateGas Estimate Gas spec', async function () { this.timeout(10000); - let { restMock, web3Mock, hapiServiceInstance, ethImpl, cacheService, mirrorNodeInstance, logger, registry } = + const { restMock, web3Mock, hapiServiceInstance, ethImpl, cacheService, mirrorNodeInstance, logger, registry } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'eth_estimateGasTest', ipAddress: '0.0.0.0' }); + async function mockContractCall( callData: IContractCallRequest, estimate: boolean, statusCode: number, result: IContractCallResponse, + requestDetails: RequestDetails, ) { const formattedData = { ...callData, estimate }; - await ethImpl.contractCallFormat(formattedData); + await ethImpl.contractCallFormat(formattedData, requestDetails); return web3Mock.onPost('contracts/call', formattedData).reply(statusCode, result); } @@ -78,9 +81,8 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = createStubInstance(SDKClient); getSdkClientStub = stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); ethImplOverridden = new EthImpl(hapiServiceInstance, mirrorNodeInstance, logger, '0x12a', registry, cacheService); @@ -108,9 +110,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gasPrice: '0x0', data: null, }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); }); @@ -120,9 +122,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', to: RECEIVER_ADDRESS, }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_CONTRACT_CALL_AVERAGE_GAS)); }); @@ -131,9 +133,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { data: '0x608060405234801561001057600080fd5b506040516107893803806107898339818101604052810190610032919061015a565b806000908051906020019061004892919061004f565b50506102f6565b82805461005b90610224565b90600052602060002090601f01602090048101928261007d57600085556100c4565b82601f1061009657805160ff19168380011785556100c4565b828001600101855582156100c4579182015b828111156100c35782518255916020019190600101906100a8565b5b5090506100d191906100d5565b5090565b5b808211156100ee5760008160009055506001016100d6565b5090565b6000610105610100846101c0565b61019b565b90508281526020810184848401111561011d57600080fd5b6101288482856101f1565b509392505050565b600082601f83011261014157600080fd5b81516101518482602086016100f2565b91505092915050565b60006020828403121561016c57600080fd5b600082015167ffffffffffffffff81111561018657600080fd5b61019284828501610130565b91505092915050565b60006101a56101b6565b90506101b18282610256565b919050565b6000604051905090565b600067ffffffffffffffff8211156101db576101da6102b6565b5b6101e4826102e5565b9050602081019050919050565b60005b8381101561020f5780820151818401526020810190506101f4565b8381111561021e576000848401525b50505050565b6000600282049050600182168061023c57607f821691505b602082108114156102505761024f610287565b5b50919050565b61025f826102e5565b810181811067ffffffffffffffff8211171561027e5761027d6102b6565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b610484806103056000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063a41368621461003b578063cfae321714610057575b600080fd5b6100556004803603810190610050919061022c565b610075565b005b61005f61008f565b60405161006c91906102a6565b60405180910390f35b806000908051906020019061008b929190610121565b5050565b60606000805461009e9061037c565b80601f01602080910402602001604051908101604052809291908181526020018280546100ca9061037c565b80156101175780601f106100ec57610100808354040283529160200191610117565b820191906000526020600020905b8154815290600101906020018083116100fa57829003601f168201915b5050505050905090565b82805461012d9061037c565b90600052602060002090601f01602090048101928261014f5760008555610196565b82601f1061016857805160ff1916838001178555610196565b82800160010185558215610196579182015b8281111561019557825182559160200191906001019061017a565b5b5090506101a391906101a7565b5090565b5b808211156101c05760008160009055506001016101a8565b5090565b60006101d76101d2846102ed565b6102c8565b9050828152602081018484840111156101ef57600080fd5b6101fa84828561033a565b509392505050565b600082601f83011261021357600080fd5b81356102238482602086016101c4565b91505092915050565b60006020828403121561023e57600080fd5b600082013567ffffffffffffffff81111561025857600080fd5b61026484828501610202565b91505092915050565b60006102788261031e565b6102828185610329565b9350610292818560208601610349565b61029b8161043d565b840191505092915050565b600060208201905081810360008301526102c0818461026d565b905092915050565b60006102d26102e3565b90506102de82826103ae565b919050565b6000604051905090565b600067ffffffffffffffff8211156103085761030761040e565b5b6103118261043d565b9050602081019050919050565b600081519050919050565b600082825260208201905092915050565b82818337600083830152505050565b60005b8381101561036757808201518184015260208101905061034c565b83811115610376576000848401525b50505050565b6000600282049050600182168061039457607f821691505b602082108114156103a8576103a76103df565b5b50919050565b6103b78261043d565b810181811067ffffffffffffffff821117156103d6576103d561040e565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f830116905091905056fea264697066735822122070d157c4efbb3fba4a1bde43cbba5b92b69f2fc455a650c0dfb61e9ed3d4bd6364736f6c634300080400330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b696e697469616c5f6d7367000000000000000000000000000000000000000000', from: '0x81cb089c285e5ee3a7353704fb114955037443af', }; - await mockContractCall(callData, true, 200, { result: `0x61A80` }); + await mockContractCall(callData, true, 200, { result: `0x61A80` }, requestDetails); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect((gas as string).toLowerCase()).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT).toLowerCase()); }); @@ -143,9 +145,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', value: 1, }; - await mockContractCall(callData, true, 200, { result: `0x61A80` }); + await mockContractCall(callData, true, 200, { result: `0x61A80` }, requestDetails); - const gas = await ethImpl.estimateGas({ ...callData, value: ONE_TINYBAR_IN_WEI_HEX }, null); + const gas = await ethImpl.estimateGas({ ...callData, value: ONE_TINYBAR_IN_WEI_HEX }, null, requestDetails); expect((gas as string).toLowerCase()).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT).toLowerCase()); }); @@ -153,9 +155,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '0x01', }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImpl.estimateGas({ data: '0x01' }, null); + const gas = await ethImpl.estimateGas({ data: '0x01' }, null, requestDetails); expect(gas).to.equal(numberTo0x(Precheck.transactionIntrinsicGasCost(callData.data!))); }); @@ -166,10 +168,12 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: '0x2540BE400', }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); - restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); + restMock + .onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`) + .reply(200, { address: RECEIVER_ADDRESS }, requestDetails); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_BASE_COST)); }); @@ -179,10 +183,12 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { from: '0x81cb089c285e5ee3a7353704fb114955037443af', to: RECEIVER_ADDRESS, }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); - restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); + restMock + .onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`) + .reply(200, { address: RECEIVER_ADDRESS }, requestDetails); - const result = await ethImpl.estimateGas(callData, null); + const result = await ethImpl.estimateGas(callData, null, requestDetails); expect(result).to.not.be.null; expect((result as JsonRpcError).code).to.eq(-32602); }); @@ -192,7 +198,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const gas = await ethImpl.estimateGas( @@ -201,6 +207,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { value: 100_000_000_000, }, null, + requestDetails, ); expect(gas).to.equal(EthImpl.gasTxBaseCost); }); @@ -210,7 +217,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(200, { address: RECEIVER_ADDRESS }); const gasBeforeCache = await ethImpl.estimateGas( @@ -219,6 +226,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { value: 100_000_000_000, }, null, + requestDetails, ); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(404); @@ -228,6 +236,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { value: 100_000_000_000, }, null, + requestDetails, ); expect(gasBeforeCache).to.equal(EthImpl.gasTxBaseCost); @@ -239,7 +248,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 10, //in tinybars }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); restMock.onGet(`accounts/${RECEIVER_ADDRESS}${NO_TRANSACTIONS}`).reply(404); const hollowAccountGasCreation = await ethImpl.estimateGas( @@ -248,6 +257,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { value: 100_000_000_000, }, null, + requestDetails, ); expect(hollowAccountGasCreation).to.equal(EthImpl.gasTxHollowAccountCreation); @@ -258,13 +268,14 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: 0, //in tinybars }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); const result = await ethImpl.estimateGas( { to: RECEIVER_ADDRESS, value: 0, }, null, + requestDetails, ); expect(result).to.exist; @@ -284,9 +295,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { input: '0x81cb089c285e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af85e5ee3a7353704fb114955037443af', }; - await mockContractCall(callData, true, 200, { result: `0x14b662` }); + await mockContractCall(callData, true, 200, { result: `0x14b662` }, requestDetails); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect((gas as string).toLowerCase()).to.equal(numberTo0x(gasEstimation).toLowerCase()); }); @@ -296,13 +307,14 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: null, //in tinybars }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); const result = await ethImpl.estimateGas( { to: RECEIVER_ADDRESS, value: null, }, null, + requestDetails, ); expect(result).to.exist; @@ -314,17 +326,17 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas empty call returns transfer cost', async function () { const callData: IContractCallRequest = {}; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImpl.estimateGas({}, null); + const gas = await ethImpl.estimateGas({}, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); }); it('should eth_estimateGas empty call returns transfer cost with overridden default gas', async function () { const callData: IContractCallRequest = {}; - await mockContractCall(callData, true, 200, { result: numberTo0x(defaultGasOverride) }); + await mockContractCall(callData, true, 200, { result: numberTo0x(defaultGasOverride) }, requestDetails); - const gas = await ethImplOverridden.estimateGas({}, null); + const gas = await ethImplOverridden.estimateGas({}, null, requestDetails); expect(gas).to.equal(numberTo0x(defaultGasOverride)); }); @@ -335,9 +347,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { }; const contractsCallResponse: IContractCallResponse = { errorMessage: '', statusCode: 501 }; - await mockContractCall(callData, true, 501, contractsCallResponse); + await mockContractCall(callData, true, 501, contractsCallResponse, requestDetails); - const gas = await ethImpl.estimateGas({ data: '' }, null); + const gas = await ethImpl.estimateGas({ data: '' }, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); }); @@ -345,9 +357,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '', }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImplOverridden.estimateGas({ data: '' }, null); + const gas = await ethImplOverridden.estimateGas({ data: '' }, null, requestDetails); expect(gas).to.equal(numberTo0x(defaultGasOverride)); }); @@ -357,10 +369,10 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { to: RECEIVER_ADDRESS, value: '0x1', }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); mockGetAccount(RECEIVER_ADDRESS, 200, { account: '0.0.1234', evm_address: RECEIVER_ADDRESS }); - const gas = await ethImpl.estimateGas(callData, null); + const gas = await ethImpl.estimateGas(callData, null, requestDetails); expect(gas).to.equal(numberTo0x(constants.TX_BASE_COST)); }); @@ -368,9 +380,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const callData: IContractCallRequest = { data: '0x0', }; - await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }); + await mockContractCall(callData, true, 501, { errorMessage: '', statusCode: 501 }, requestDetails); - const gas = await ethImplOverridden.estimateGas({ data: '0x' }, null); + const gas = await ethImplOverridden.estimateGas({ data: '0x' }, null, requestDetails); expect(gas).to.equal(numberTo0x(defaultGasOverride)); }); @@ -386,9 +398,9 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { ], }, }; - await mockContractCall(transaction, true, 400, contractCallResult); + await mockContractCall(transaction, true, 400, contractCallResult, requestDetails); - const estimatedGas = await ethImpl.estimateGas(transaction, id); + const estimatedGas = await ethImpl.estimateGas(transaction, id, requestDetails); expect(estimatedGas).to.equal(numberTo0x(Precheck.transactionIntrinsicGasCost(transaction.data!))); }); @@ -396,38 +408,50 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { it('should eth_estimateGas with contract revert and message does not equal executionReverted and ESTIMATE_GAS_THROWS is set to false', async function () { const estimateGasThrows = process.env.ESTIMATE_GAS_THROWS; process.env.ESTIMATE_GAS_THROWS = 'false'; - await mockContractCall(transaction, true, 400, { - _status: { - messages: [ - { - message: 'data field invalid hexadecimal string', - detail: '', - data: '', - }, - ], + await mockContractCall( + transaction, + true, + 400, + { + _status: { + messages: [ + { + message: 'data field invalid hexadecimal string', + detail: '', + data: '', + }, + ], + }, }, - }); + requestDetails, + ); - const result: any = await ethImpl.estimateGas(transaction, id); + const result: any = await ethImpl.estimateGas(transaction, id, requestDetails); expect(result).to.equal(numberTo0x(Precheck.transactionIntrinsicGasCost(transaction.data!))); process.env.ESTIMATE_GAS_THROWS = estimateGasThrows; }); it('should eth_estimateGas with contract revert and message equals "execution reverted: Invalid number of recipients"', async function () { - await mockContractCall(transaction, true, 400, { - _status: { - messages: [ - { - data: '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c496e76616c6964206e756d626572206f6620726563697069656e747300000000', - detail: 'Invalid number of recipients', - message: 'CONTRACT_REVERT_EXECUTED', - }, - ], + await mockContractCall( + transaction, + true, + 400, + { + _status: { + messages: [ + { + data: '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c496e76616c6964206e756d626572206f6620726563697069656e747300000000', + detail: 'Invalid number of recipients', + message: 'CONTRACT_REVERT_EXECUTED', + }, + ], + }, }, - }); + requestDetails, + ); - const result: any = await ethImpl.estimateGas(transaction, id); + const result: any = await ethImpl.estimateGas(transaction, id, requestDetails); expect(result.data).to.equal( '0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001c496e76616c6964206e756d626572206f6620726563697069656e747300000000', @@ -441,19 +465,25 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const encodedMessage = new AbiCoder().encode(['string'], [decodedMessage]).replace('0x', ''); const encodedCustomError = customErrorSignature + encodedMessage; - await mockContractCall(transaction, true, 400, { - _status: { - messages: [ - { - message: 'CONTRACT_REVERT_EXECUTED', - detail: decodedMessage, - data: encodedCustomError, - }, - ], + await mockContractCall( + transaction, + true, + 400, + { + _status: { + messages: [ + { + message: 'CONTRACT_REVERT_EXECUTED', + detail: decodedMessage, + data: encodedCustomError, + }, + ], + }, }, - }); + requestDetails, + ); - const result: any = await ethImpl.estimateGas(transaction, id); + const result: any = await ethImpl.estimateGas(transaction, id, requestDetails); expect(result.data).to.equal(encodedCustomError); expect(result.message).to.equal(`execution reverted: ${decodedMessage}`); @@ -465,38 +495,50 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { const encodedMessage = new AbiCoder().encode(['string'], [decodedMessage]).replace('0x', ''); const encodedGenericError = defaultErrorSignature + encodedMessage; - await mockContractCall(transaction, true, 400, { - _status: { - messages: [ - { - message: 'CONTRACT_REVERT_EXECUTED', - detail: decodedMessage, - data: encodedGenericError, - }, - ], + await mockContractCall( + transaction, + true, + 400, + { + _status: { + messages: [ + { + message: 'CONTRACT_REVERT_EXECUTED', + detail: decodedMessage, + data: encodedGenericError, + }, + ], + }, }, - }); + requestDetails, + ); - const result: any = await ethImpl.estimateGas(transaction, id); + const result: any = await ethImpl.estimateGas(transaction, id, requestDetails); expect(result.data).to.equal(encodedGenericError); expect(result.message).to.equal(`execution reverted: ${decodedMessage}`); }); it('should eth_estimateGas handles a 501 unimplemented response from the mirror node correctly by returning default gas', async function () { - await mockContractCall(transaction, true, 501, { - _status: { - messages: [ - { - message: 'Auto account creation is not supported.', - detail: '', - data: '', - }, - ], + await mockContractCall( + transaction, + true, + 501, + { + _status: { + messages: [ + { + message: 'Auto account creation is not supported.', + detail: '', + data: '', + }, + ], + }, }, - }); + requestDetails, + ); - const result: any = await ethImpl.estimateGas({ ...transaction, data: '0x', value: '0x1' }, id); + const result: any = await ethImpl.estimateGas({ ...transaction, data: '0x', value: '0x1' }, id, requestDetails); expect(result).to.equal(numberTo0x(constants.TX_DEFAULT_GAS_DEFAULT)); }); @@ -509,7 +551,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gas: '0xd97010', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.value).to.eq(1110); expect(transaction.gasPrice).to.eq(1000000); expect(transaction.gas).to.eq(14250000); @@ -527,7 +569,7 @@ describe('@ethEstimateGas Estimate Gas spec', async function () { gas: '0xd97010', }; - await ethImpl.contractCallFormat(transaction); + await ethImpl.contractCallFormat(transaction, requestDetails); expect(transaction.data).to.eq(inputValue); expect(transaction.data).to.not.eq(dataValue); expect(transaction.input).to.be.undefined; diff --git a/packages/relay/tests/lib/eth/eth_feeHistory.spec.ts b/packages/relay/tests/lib/eth/eth_feeHistory.spec.ts index dca3b5f67e..be4b57b667 100644 --- a/packages/relay/tests/lib/eth/eth_feeHistory.spec.ts +++ b/packages/relay/tests/lib/eth/eth_feeHistory.spec.ts @@ -38,23 +38,25 @@ import { } from './eth-config'; import { numberTo0x } from '../../../src/formatters'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethFeeHistory using MirrorNode', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'eth_feeHistoryTest', ipAddress: '0.0.0.0' }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -95,7 +97,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { it('eth_feeHistory', async function () { previousFees.fees[2].gas += 1; - const feeHistory = await ethImpl.feeHistory(2, 'latest', [25, 75]); + const feeHistory = await ethImpl.feeHistory(2, 'latest', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['baseFeePerGas'].length).to.equal(3); @@ -111,38 +113,38 @@ describe('@ethFeeHistory using MirrorNode', async function () { }); it('eth_feeHistory with latest param', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'latest', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'latest', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + BLOCK_NUMBER_3); }); it('eth_feeHistory with pending param', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'pending', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'pending', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + BLOCK_NUMBER_3); }); it('eth_feeHistory with finalized param', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'finalized', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'finalized', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + BLOCK_NUMBER_3); }); it('eth_feeHistory with safe param', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'safe', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'safe', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + BLOCK_NUMBER_3); }); it('eth_feeHistory with earliest param', async function () { const firstBlockIndex = 0; - const feeHistory = await ethImpl.feeHistory(1, 'earliest', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'earliest', [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + firstBlockIndex); }); it('eth_feeHistory with number param', async function () { - const feeHistory = await ethImpl.feeHistory(1, '0x' + BLOCK_NUMBER_3, [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, '0x' + BLOCK_NUMBER_3, [25, 75], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq('0x' + BLOCK_NUMBER_3); }); @@ -157,7 +159,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { restMock.onGet(`blocks/${blockNumber}`).reply(200, { ...DEFAULT_BLOCK, number: blockNumber }), ); - const feeHistory = await ethImpl.feeHistory(200, '0x9', [0]); + const feeHistory = await ethImpl.feeHistory(200, '0x9', [0], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.equal(`0x0`); @@ -175,8 +177,8 @@ describe('@ethFeeHistory using MirrorNode', async function () { restMock.onGet(`blocks/${latestBlock.number}`).reply(200, latestBlock); restMock.onGet(`network/fees?timestamp=lte:${latestBlock.timestamp.to}`).reply(200, latestFees); - const firstFeeHistory = await ethImpl.feeHistory(1, hexBlockNumber, null); - const secondFeeHistory = await ethImpl.feeHistory(1, hexBlockNumber, null); + const firstFeeHistory = await ethImpl.feeHistory(1, hexBlockNumber, null, requestDetails); + const secondFeeHistory = await ethImpl.feeHistory(1, hexBlockNumber, null, requestDetails); expect(firstFeeHistory).to.exist; expect(firstFeeHistory['baseFeePerGas'][0]).to.equal(BASE_FEE_PER_GAS_HEX); @@ -199,7 +201,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { } this.beforeEach(() => { - sdkClientStub.getTinyBarGasFee.returns(fauxGasTinyBars); + sdkClientStub.getTinyBarGasFee.resolves(fauxGasTinyBars); restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [latestBlock] }); restMock.onGet(`blocks/${latestBlock.number}`).reply(200, latestBlock); restMock.onGet(`network/fees?timestamp=lte:${latestBlock.timestamp.to}`).reply(404, NOT_FOUND_RES); @@ -207,7 +209,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { }); it('eth_feeHistory on mirror 404', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'latest', [25, 75]); + const feeHistory = await ethImpl.feeHistory(1, 'latest', [25, 75], requestDetails); feeHistoryOnErrorExpect(feeHistory); const rewards = feeHistory['reward'][0]; expect(rewards[0]).to.equal('0x0'); @@ -215,7 +217,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { }); it('eth_feeHistory on mirror 500', async function () { - const feeHistory = await ethImpl.feeHistory(1, 'latest', null); + const feeHistory = await ethImpl.feeHistory(1, 'latest', null, requestDetails); feeHistoryOnErrorExpect(feeHistory); }); }); @@ -240,7 +242,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { }); this.beforeEach(function () { - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); restMock.onGet(`network/fees`).reply(200, DEFAULT_NETWORK_FEES); }); @@ -256,7 +258,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { const countBlocks = 2; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', [25, 75]); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', [25, 75], requestDetails); checkCommonFeeHistoryFields(feeHistory); expect(feeHistory['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); @@ -270,7 +272,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { const countBlocks = 5; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', []); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', [], requestDetails); checkCommonFeeHistoryFields(feeHistory); expect(feeHistory['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); @@ -284,7 +286,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { const countBlocks = 5; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', []); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', [], requestDetails); checkCommonFeeHistoryFields(feeHistory); expect(feeHistory['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); @@ -298,7 +300,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { const countBlocks = 5; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'pending', []); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'pending', [], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); @@ -312,7 +314,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { restMock.onGet(`blocks/1`).reply(200, latestBlock); const countBlocks = 1; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'earliest', []); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'earliest', [], requestDetails); expect(feeHistory).to.exist; expect(feeHistory['oldestBlock']).to.eq(numberTo0x(1)); @@ -328,7 +330,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { const countBlocks = 2; - const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', []); + const feeHistory = await ethImpl.feeHistory(countBlocks, 'latest', [], requestDetails); checkCommonFeeHistoryFields(feeHistory); expect(feeHistory['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); @@ -337,7 +339,7 @@ describe('@ethFeeHistory using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(404, {}); restMock.onGet(`blocks/${latestBlock.number}`).reply(404, {}); - const feeHistoryUsingCache = await ethImpl.feeHistory(countBlocks, 'latest', []); + const feeHistoryUsingCache = await ethImpl.feeHistory(countBlocks, 'latest', [], requestDetails); checkCommonFeeHistoryFields(feeHistoryUsingCache); expect(feeHistoryUsingCache['oldestBlock']).to.eq(numberTo0x(latestBlockNumber - countBlocks + 1)); expect(feeHistoryUsingCache['baseFeePerGas'].length).to.eq(countBlocks + 1); diff --git a/packages/relay/tests/lib/eth/eth_gasPrice.spec.ts b/packages/relay/tests/lib/eth/eth_gasPrice.spec.ts index e9f9e456f2..17f836b25a 100644 --- a/packages/relay/tests/lib/eth/eth_gasPrice.spec.ts +++ b/packages/relay/tests/lib/eth/eth_gasPrice.spec.ts @@ -27,27 +27,29 @@ import constants from '../../../src/lib/constants'; import { SDKClient } from '../../../src/lib/clients'; import { numberTo0x } from '../../../dist/formatters'; import { DEFAULT_NETWORK_FEES, NOT_FOUND_RES } from './eth-config'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { predefined } from '../../../src'; import RelayAssertions from '../../assertions'; import { generateEthTestEnv } from './eth-helpers'; import { toHex } from '../../helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGasPrice Gas Price spec', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'eth_getPriceTest', ipAddress: '0.0.0.0' }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -63,20 +65,20 @@ describe('@ethGasPrice Gas Price spec', async function () { describe('@ethGasPrice', async function () { it('eth_gasPrice', async function () { - const weiBars = await ethImpl.gasPrice(); + const weiBars = await ethImpl.gasPrice(requestDetails); const expectedWeiBars = DEFAULT_NETWORK_FEES.fees[2].gas * constants.TINYBAR_TO_WEIBAR_COEF; expect(weiBars).to.equal(numberTo0x(expectedWeiBars)); }); it('eth_gasPrice with cached value', async function () { - const firstGasResult = await ethImpl.gasPrice(); + const firstGasResult = await ethImpl.gasPrice(requestDetails); const modifiedNetworkFees = { ...DEFAULT_NETWORK_FEES }; modifiedNetworkFees.fees[2].gas = DEFAULT_NETWORK_FEES.fees[2].gas * 100; restMock.onGet(`network/fees`).reply(200, modifiedNetworkFees); - const secondGasResult = await ethImpl.gasPrice(); + const secondGasResult = await ethImpl.gasPrice(requestDetails); expect(firstGasResult).to.equal(secondGasResult); }); @@ -88,7 +90,9 @@ describe('@ethGasPrice Gas Price spec', async function () { restMock.onGet(`network/fees`).reply(200, partialNetworkFees); - await RelayAssertions.assertRejection(predefined.COULD_NOT_ESTIMATE_GAS_PRICE, ethImpl.gasPrice, true, ethImpl); + await RelayAssertions.assertRejection(predefined.COULD_NOT_ESTIMATE_GAS_PRICE, ethImpl.gasPrice, true, ethImpl, [ + requestDetails, + ]); }); describe('@ethGasPrice different value for GAS_PRICE_PERCENTAGE_BUFFER env', async function () { @@ -101,12 +105,12 @@ describe('@ethGasPrice Gas Price spec', async function () { for (let testCaseName in GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES) { it(testCaseName, async function () { const GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER_TESTCASES[testCaseName]; - const initialGasPrice = await ethImpl.gasPrice(); + const initialGasPrice = await ethImpl.gasPrice(requestDetails); process.env.GAS_PRICE_PERCENTAGE_BUFFER = GAS_PRICE_PERCENTAGE_BUFFER; - await cacheService.clear(); + await cacheService.clear(requestDetails); - const gasPriceWithBuffer = await ethImpl.gasPrice(); + const gasPriceWithBuffer = await ethImpl.gasPrice(requestDetails); process.env.GAS_PRICE_PERCENTAGE_BUFFER = '0'; const expectedInitialGasPrice = toHex(DEFAULT_NETWORK_FEES.fees[2].gas * constants.TINYBAR_TO_WEIBAR_COEF); @@ -135,14 +139,20 @@ describe('@ethGasPrice Gas Price spec', async function () { it('eth_gasPrice with mirror node return network fees found', async function () { const fauxGasTinyBars = 35_000; const fauxGasWeiBarHex = '0x13e52b9abe000'; - sdkClientStub.getTinyBarGasFee.returns(fauxGasTinyBars); + sdkClientStub.getTinyBarGasFee.resolves(fauxGasTinyBars); - const gas = await ethImpl.gasPrice(); + const gas = await ethImpl.gasPrice(requestDetails); expect(gas).to.equal(fauxGasWeiBarHex); }); it('eth_gasPrice with no network fees records found', async function () { - await RelayAssertions.assertRejection(predefined.COULD_NOT_ESTIMATE_GAS_PRICE, ethImpl.gasPrice, true, ethImpl); + await RelayAssertions.assertRejection( + predefined.COULD_NOT_ESTIMATE_GAS_PRICE, + ethImpl.gasPrice, + true, + ethImpl, + [requestDetails], + ); }); }); }); diff --git a/packages/relay/tests/lib/eth/eth_getBalance.spec.ts b/packages/relay/tests/lib/eth/eth_getBalance.spec.ts index 274157f83d..3af1eb1b5a 100644 --- a/packages/relay/tests/lib/eth/eth_getBalance.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBalance.spec.ts @@ -24,7 +24,7 @@ import sinon from 'sinon'; import chaiAsPromised from 'chai-as-promised'; import { EthImpl } from '../../../src/lib/eth'; -import { buildCryptoTransferTransaction, getRequestId } from '../../helpers'; +import { buildCryptoTransferTransaction } from '../../helpers'; import { SDKClient } from '../../../src/lib/clients'; import { numberTo0x } from '../../../dist/formatters'; import { @@ -44,21 +44,24 @@ import { TINYBAR_TO_WEIBAR_COEF_BIGINT, } from './eth-config'; import { balancesByAccountIdByTimestampURL, generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGetBalance using MirrorNode', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'eth_getBalanceTest', ipAddress: '0.0.0.0' }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -78,7 +81,7 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOCK_BLOCK_NUMBER_1000_RES); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); }); @@ -86,13 +89,13 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOCK_BLOCK_NUMBER_1000_RES); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); // next call should use cache restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, {}); - const resBalanceCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null); + const resBalanceCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(resBalanceCached).to.equal(resBalance); // Third call should return new number using mirror node @@ -105,9 +108,9 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); // expire cache, instead of waiting for ttl we clear it to simulate expiry faster. - cacheService.clear(); + await cacheService.clear(requestDetails); - const resBalanceNew = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resBalanceNew = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(newBalanceHex).to.equal(resBalanceNew); }); @@ -116,7 +119,7 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOCK_BLOCKS_FOR_BALANCE_RES); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); }); @@ -128,7 +131,7 @@ describe('@ethGetBalance using MirrorNode', async function () { }); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockHash, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockHash, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); }); @@ -137,7 +140,7 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOCK_BLOCKS_FOR_BALANCE_RES); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); }); @@ -160,7 +163,7 @@ describe('@ethGetBalance using MirrorNode', async function () { ], }); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockHash, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockHash, requestDetails); expect(resBalance).to.equal(DEF_HEX_BALANCE); }); @@ -169,7 +172,7 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(`contracts/${CONTRACT_ADDRESS_1}`).reply(200, null); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, NOT_FOUND_RES); - const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(resBalance).to.equal(EthImpl.zeroHex); }); @@ -177,11 +180,11 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOCK_BLOCK_NUMBER_1000_RES); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(200, MOCK_BALANCE_RES); - const resNoCache = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resNoCache = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, NOT_FOUND_RES); - const resCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, getRequestId()); + const resCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, null, requestDetails); expect(resNoCache).to.equal(DEF_HEX_BALANCE); expect(resCached).to.equal(DEF_HEX_BALANCE); }); @@ -217,11 +220,11 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); - const resNoCache = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, getRequestId()); + const resNoCache = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, requestDetails); restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, NOT_FOUND_RES); - const resCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, getRequestId()); + const resCached = await ethImpl.getBalance(CONTRACT_ADDRESS_1, blockNumber, requestDetails); expect(resNoCache).to.equal(DEF_HEX_BALANCE); expect(resCached).to.equal(DEF_HEX_BALANCE); }); @@ -333,27 +336,27 @@ describe('@ethGetBalance using MirrorNode', async function () { }); it('latest', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'latest', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'latest', requestDetails); expect(resBalance).to.equal(hexBalance3); }); it('finalized', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'finalized', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'finalized', requestDetails); expect(resBalance).to.equal(hexBalance3); }); it('safe', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'safe', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'safe', requestDetails); expect(resBalance).to.equal(hexBalance3); }); it('earliest', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'earliest', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'earliest', requestDetails); expect(resBalance).to.equal('0x0'); }); it('pending', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'pending', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, 'pending', requestDetails); expect(resBalance).to.equal(hexBalance3); }); @@ -387,7 +390,7 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -402,7 +405,7 @@ describe('@ethGetBalance using MirrorNode', async function () { transactions: [], }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', requestDetails); expect(resBalance).to.equal(hexBalance1); }); @@ -433,7 +436,7 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 - 175) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -465,7 +468,7 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 + 175) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -498,7 +501,7 @@ describe('@ethGetBalance using MirrorNode', async function () { }, }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '2', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 + 65) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -543,7 +546,7 @@ describe('@ethGetBalance using MirrorNode', async function () { ], }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 - 230) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -604,7 +607,7 @@ describe('@ethGetBalance using MirrorNode', async function () { blocks: [latestBlock], }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 - 480) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); @@ -664,13 +667,13 @@ describe('@ethGetBalance using MirrorNode', async function () { blocks: [latestBlock], }); - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '1', requestDetails); const historicalBalance = numberTo0x(BigInt(balance3 - 80) * TINYBAR_TO_WEIBAR_COEF_BIGINT); expect(resBalance).to.equal(historicalBalance); }); it('blockNumber is the same as the latest block', async () => { - const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '3', getRequestId()); + const resBalance = await ethImpl.getBalance(CONTRACT_ID_1, '3', requestDetails); expect(resBalance).to.equal(hexBalance3); }); @@ -681,7 +684,7 @@ describe('@ethGetBalance using MirrorNode', async function () { .onGet(balancesByAccountIdByTimestampURL(notFoundEvmAddress, '1651550386.060890949')) .reply(404, NOT_FOUND_RES); - const resBalance = await ethImpl.getBalance(notFoundEvmAddress, '1', getRequestId()); + const resBalance = await ethImpl.getBalance(notFoundEvmAddress, '1', requestDetails); expect(resBalance).to.equal(EthImpl.zeroHex); }); @@ -700,7 +703,7 @@ describe('@ethGetBalance using MirrorNode', async function () { restMock.onGet(`blocks/2`).reply(200, recentBlockWithinLastfifteen); restMock.onGet(`accounts/${notFoundEvmAddress}?limit=100`).reply(404, NOT_FOUND_RES); - const resBalance = await ethImpl.getBalance(notFoundEvmAddress, '2', getRequestId()); + const resBalance = await ethImpl.getBalance(notFoundEvmAddress, '2', requestDetails); expect(resBalance).to.equal(EthImpl.zeroHex); }); }); diff --git a/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts index d2ea6e35e6..dcf2eb6074 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts @@ -23,7 +23,7 @@ import { expect, use } from 'chai'; import sinon from 'sinon'; import chaiAsPromised from 'chai-as-promised'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { predefined } from '../../../src'; import { EthImpl } from '../../../src/lib/eth'; import { blockLogsBloom, defaultContractResults, defaultDetailedContractResults } from '../../helpers'; import { SDKClient } from '../../../src/lib/clients'; @@ -56,12 +56,13 @@ import { DEFAULT_BLOCK_RECEIPTS_ROOT_HASH, } from './eth-config'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; let ethImplLowTransactionCount: EthImpl; @@ -72,11 +73,12 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { const results = defaultContractResults.results; const TOTAL_GAS_USED = numberTo0x(results[0].gas_used + results[1].gas_used); + const requestDetails = new RequestDetails({ requestId: 'eth_getBlockByHashTest', ipAddress: '0.0.0.0' }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -113,7 +115,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, gasUsed: TOTAL_GAS_USED, @@ -133,7 +135,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const res = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const res = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); RelayAssertions.assertBlock(res, { transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2], hash: BLOCK_HASH_TRIMMED, @@ -154,7 +156,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, gasUsed: TOTAL_GAS_USED, @@ -176,7 +178,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, gasUsed: TOTAL_GAS_USED, @@ -194,7 +196,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); for (let i = 0; i < 3; i++) { - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); expect(result).to.exist; expect(result).to.not.be.null; if (result) { @@ -211,7 +213,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, true); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, true, requestDetails); RelayAssertions.assertBlock( result, { @@ -234,7 +236,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { restMock.onGet(CONTRACTS_RESULTS_NEXT_URL).reply(200, defaultContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, true); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, true, requestDetails); RelayAssertions.assertBlock( result, { @@ -251,7 +253,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { }); it('eth_getBlockByHash with block match and contract revert', async function () { - cacheService.clear(); + await cacheService.clear(requestDetails); const randomBlock = { ...DEFAULT_BLOCK, gas_used: 400000, @@ -268,7 +270,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { ) .reply(200, { logs: [] }); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, true); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, true, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, gasUsed: numberTo0x(randomBlock.gas_used), @@ -280,11 +282,11 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { }); it('eth_getBlockByHash with no match', async function () { - cacheService.clear(); + await cacheService.clear(requestDetails); // mirror node request mocks restMock.onGet(`blocks/${BLOCK_HASH}`).reply(404, NO_SUCH_BLOCK_EXISTS_RES); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); expect(result).to.equal(null); }); @@ -305,6 +307,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { await RelayAssertions.assertRejection(predefined.INTERNAL_ERROR(), ethImpl.getBlockByHash, false, ethImpl, [ BLOCK_HASH, false, + requestDetails, ]); }); @@ -320,7 +323,7 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { .reply(200, defaultDetailedContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const args = [BLOCK_HASH, true]; + const args = [BLOCK_HASH, true, requestDetails]; await RelayAssertions.assertRejection( predefined.MAX_BLOCK_SIZE(77), diff --git a/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts index ba3da3da1f..4b29f5272a 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts @@ -23,11 +23,11 @@ import { expect, use } from 'chai'; import sinon from 'sinon'; import chaiAsPromised from 'chai-as-promised'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { Eth, predefined } from '../../../src'; import { EthImpl } from '../../../src/lib/eth'; import { blockLogsBloom, defaultContractResults, defaultDetailedContractResults } from '../../helpers'; import { Block, Transaction } from '../../../src/lib/model'; -import { SDKClient } from '../../../src/lib/clients'; +import { MirrorNodeClient, SDKClient } from '../../../src/lib/clients'; import RelayAssertions from '../../assertions'; import constants from '../../../src/lib/constants'; import { hashNumber, numberTo0x } from '../../../dist/formatters'; @@ -73,22 +73,44 @@ import { } from './eth-config'; import { generateEthTestEnv } from './eth-helpers'; import { fail } from 'assert'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; +import { Registry } from 'prom-client'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; let ethImplLowTransactionCount: EthImpl; describe('@ethGetBlockByNumber using MirrorNode', async function () { this.timeout(10000); - let { restMock, hapiServiceInstance, ethImpl, cacheService, mirrorNodeInstance, logger, registry } = - generateEthTestEnv(true); + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + mirrorNodeInstance, + logger, + registry, + }: { + restMock: MockAdapter; + hapiServiceInstance: HAPIService; + ethImpl: Eth; + cacheService: CacheService; + mirrorNodeInstance: MirrorNodeClient; + logger: any; + registry: Registry; + } = generateEthTestEnv(true); const results = defaultContractResults.results; const TOTAL_GAS_USED = numberTo0x(results[0].gas_used + results[1].gas_used); + const requestDetails = new RequestDetails({ requestId: 'eth_getBlockByNumberTest', ipAddress: '0.0.0.0' }); + const veriftAggregatedInfo = (result) => { // verify aggregated info expect(result).to.exist; @@ -106,9 +128,9 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { expect(transactions[1].gas).equal(hashNumber(GAS_USED_2)); } - this.beforeEach(() => { + this.beforeEach(async () => { // reset cache and restMock - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.reset(); restMock.resetHandlers(); @@ -146,36 +168,36 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { it('"eth_blockNumber" should return the latest block number', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); - const blockNumber = await ethImpl.blockNumber(); + const blockNumber = await ethImpl.blockNumber(requestDetails); expect(blockNumber).to.be.eq(blockNumber); }); it('"eth_blockNumber" should return the latest block number using cache', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); - const blockNumber = await ethImpl.blockNumber(); + const blockNumber = await ethImpl.blockNumber(requestDetails); expect(numberTo0x(DEFAULT_BLOCK.number)).to.be.eq(blockNumber); // Second call should return the same block number using cache restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(400, DEFAULT_BLOCKS_RES); - const blockNumber2 = await ethImpl.blockNumber(); + const blockNumber2 = await ethImpl.blockNumber(requestDetails); expect(blockNumber2).to.be.eq(blockNumber); // expire cache, instead of waiting for ttl we clear it to simulate expiry faster. - cacheService.clear(); + await cacheService.clear(requestDetails); // Third call should return new number using mirror node const newBlockNumber = 7; restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [{ ...DEFAULT_BLOCK, number: newBlockNumber }], }); - const blockNumber3 = await ethImpl.blockNumber(); + const blockNumber3 = await ethImpl.blockNumber(requestDetails); expect(numberTo0x(newBlockNumber)).to.be.eq(blockNumber3); }); it('"eth_blockNumber" should throw an error if no blocks are found', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(404, BLOCK_NOT_FOUND_RES); const error = predefined.COULD_NOT_RETRIEVE_LATEST_BLOCK; - await RelayAssertions.assertRejection(error, ethImpl.blockNumber, true, ethImpl); + await RelayAssertions.assertRejection(error, ethImpl.blockNumber, true, ethImpl, [requestDetails]); }); it('"eth_blockNumber" return the latest block number on second try', async function () { @@ -186,9 +208,9 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { .replyOnce(200, DEFAULT_BLOCKS_RES); try { - await ethImpl.blockNumber(); + await ethImpl.blockNumber(requestDetails); } catch (error) {} - const blockNumber = await ethImpl.blockNumber(); + const blockNumber = await ethImpl.blockNumber(requestDetails); expect(blockNumber).to.be.eq(blockNumber); }); @@ -201,6 +223,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { ethImpl.blockNumber, true, ethImpl, + [requestDetails], ); }); @@ -216,7 +239,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { it('eth_getBlockByNumber with match', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, @@ -234,7 +257,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { results: [...defaultContractResults.results, ...defaultContractResults.results], }); - const res = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const res = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); RelayAssertions.assertBlock(res, { transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2], hash: BLOCK_HASH_TRIMMED, @@ -252,7 +275,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { }); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, @@ -270,7 +293,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { it('eth_getBlockByNumber with match paginated', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, LINKS_NEXT_RES); restMock.onGet(CONTRACTS_RESULTS_NEXT_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, @@ -285,10 +308,10 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { it('eth_getBlockByNumber should return cached result', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); - const resBeforeCache = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const resBeforeCache = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(404); - const resAfterCache = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const resAfterCache = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); expect(resBeforeCache).to.eq(resAfterCache); }); @@ -296,7 +319,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { it('eth_getBlockByHash with match', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); RelayAssertions.assertBlock(result, { hash: BLOCK_HASH_TRIMMED, gasUsed: TOTAL_GAS_USED, @@ -311,7 +334,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, LINKS_NEXT_RES); restMock.onGet(CONTRACTS_RESULTS_NEXT_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false); + const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); const toMatch = { hash: BLOCK_HASH_TRIMMED, gasUsed: TOTAL_GAS_USED, @@ -330,7 +353,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, { results: [] }); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, { logs: [] }); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), false, requestDetails); if (result) { // verify aggregated info veriftAggregatedInfo(result); @@ -349,7 +372,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true, requestDetails); if (result) { veriftAggregatedInfo(result); expect(result.gasUsed).equal(TOTAL_GAS_USED); @@ -369,7 +392,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(CONTRACT_QUERY).reply(200, CONTRACT_RESPONSE_MOCK); restMock.onGet(LOG_QUERY).reply(200, LOGS_RESPONSE_MOCK); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER_WITH_SYN_TXN), true); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER_WITH_SYN_TXN), true, requestDetails); if (result) { result.transactions.forEach((txn) => { expect(txn.maxFeePerGas).to.exist; @@ -387,7 +410,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, LINKS_NEXT_RES); restMock.onGet(CONTRACTS_RESULTS_NEXT_URL).reply(200, defaultContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true, requestDetails); if (result) { // verify aggregated info veriftAggregatedInfo(result); @@ -406,7 +429,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, DEFAULT_CONTRACT_RES_REVERT); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, { logs: [] }); - const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true); + const result = await ethImpl.getBlockByNumber(numberTo0x(BLOCK_NUMBER), true, requestDetails); if (result) { veriftAggregatedInfo(result); expect(result.gasUsed).equal(numberTo0x(GAS_USED_1)); @@ -421,7 +444,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(404, NO_SUCH_BLOCK_EXISTS_RES); restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, MOST_RECENT_BLOCK); - const result = await ethImpl.getBlockByNumber(BLOCK_NUMBER.toString(), false); + const result = await ethImpl.getBlockByNumber(BLOCK_NUMBER.toString(), false, requestDetails); expect(result).to.equal(null); }); @@ -436,7 +459,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { } } - this.beforeEach(() => { + beforeEach(() => { restMock.resetHistory(); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); for (const result of defaultContractResults.results) { @@ -447,7 +470,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { }); it('eth_getBlockByNumber with latest tag', async function () { - const result = await ethImpl.getBlockByNumber('latest', false); + const result = await ethImpl.getBlockByNumber('latest', false, requestDetails); // check that we only made the expected number of requests with the expected urls expect(restMock.history.get.length).equal(TOTAL_GET_CALLS_EXECUTED); expect(restMock.history.get[0].url).equal(BLOCKS_LIMIT_ORDER_URL); @@ -465,24 +488,24 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, LINKS_NEXT_RES); restMock.onGet(CONTRACTS_RESULTS_NEXT_URL).reply(200, defaultContractResults); - const result = await ethImpl.getBlockByNumber('latest', false); + const result = await ethImpl.getBlockByNumber('latest', false, requestDetails); confirmResult(result); }); it('eth_getBlockByNumber with pending tag', async function () { - const result = await ethImpl.getBlockByNumber('pending', false); + const result = await ethImpl.getBlockByNumber('pending', false, requestDetails); confirmResult(result); }); it('eth_getBlockByNumber with earliest tag', async function () { restMock.onGet(`blocks/0`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockByNumber('earliest', false); + const result = await ethImpl.getBlockByNumber('earliest', false, requestDetails); confirmResult(result); }); it('eth_getBlockByNumber with finalized tag', async function () { - const result = await ethImpl.getBlockByNumber('finalized', false); + const result = await ethImpl.getBlockByNumber('finalized', false, requestDetails); // check that we only made the expected number of requests with the expected urls expect(restMock.history.get.length).equal(TOTAL_GET_CALLS_EXECUTED); expect(restMock.history.get[0].url).equal(BLOCKS_LIMIT_ORDER_URL); @@ -497,7 +520,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { }); it('eth_getBlockByNumber with safe tag', async function () { - const result = await ethImpl.getBlockByNumber('safe', false); + const result = await ethImpl.getBlockByNumber('safe', false, requestDetails); // check that we only made the expected number of requests with the expected urls expect(restMock.history.get.length).equal(TOTAL_GET_CALLS_EXECUTED); expect(restMock.history.get[0].url).equal(BLOCKS_LIMIT_ORDER_URL); @@ -515,7 +538,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { restMock.onGet(`blocks/3735929054`).reply(200, DEFAULT_BLOCK); restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, BLOCKS_RES); - const result = await ethImpl.getBlockByNumber('0xdeadc0de', false); + const result = await ethImpl.getBlockByNumber('0xdeadc0de', false, requestDetails); confirmResult(result); }); }); @@ -533,7 +556,7 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { .reply(200, defaultDetailedContractResults); restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, DEFAULT_ETH_GET_BLOCK_BY_LOGS); - const args = [numberTo0x(BLOCK_NUMBER), true]; + const args = [numberTo0x(BLOCK_NUMBER), true, requestDetails]; await RelayAssertions.assertRejection( predefined.MAX_BLOCK_SIZE(77), diff --git a/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByHash.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByHash.spec.ts index cf29a97be1..e626de8bbb 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByHash.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByHash.spec.ts @@ -33,20 +33,26 @@ import { NO_SUCH_BLOCK_EXISTS_RES, } from './eth-config'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; describe('@ethGetBlockTransactionCountByHash using MirrorNode', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ + requestId: 'eth_getBlockTransactionCountByHashTest', + ipAddress: '0.0.0.0', + }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -63,7 +69,7 @@ describe('@ethGetBlockTransactionCountByHash using MirrorNode', async function ( // mirror node request mocks restMock.onGet(`blocks/${BLOCK_HASH}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH); + const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH, requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -71,7 +77,7 @@ describe('@ethGetBlockTransactionCountByHash using MirrorNode', async function ( restMock.onGet(`blocks/${BLOCK_HASH}`).replyOnce(200, DEFAULT_BLOCK); for (let i = 0; i < 3; i++) { - const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH); + const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH, requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); } }); @@ -80,7 +86,7 @@ describe('@ethGetBlockTransactionCountByHash using MirrorNode', async function ( // mirror node request mocks restMock.onGet(`blocks/${BLOCK_HASH}`).reply(404, NO_SUCH_BLOCK_EXISTS_RES); - const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH); + const result = await ethImpl.getBlockTransactionCountByHash(BLOCK_HASH, requestDetails); expect(result).to.equal(null); }); }); diff --git a/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByNumber.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByNumber.spec.ts index 188acf8dcb..27c579b42f 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByNumber.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockTransactionCountByNumber.spec.ts @@ -35,22 +35,27 @@ import { NO_SUCH_BLOCK_EXISTS_RES, } from './eth-config'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); - this.beforeEach(() => { + const requestDetails = new RequestDetails({ + requestId: 'eth_getBlockTransactionCountByNumberTest', + ipAddress: '0.0.0.0', + }); + + this.beforeEach(async () => { // reset cache and restMock - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -65,7 +70,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function // mirror node request mocks restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString()); + const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString(), requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -73,16 +78,16 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function restMock.onGet(`blocks/${BLOCK_NUMBER}`).replyOnce(200, DEFAULT_BLOCK); for (let i = 0; i < 3; i++) { - const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString()); + const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString(), requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); } }); it('eth_getBlockTransactionCountByNumber with no match', async function () { - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(404, NO_SUCH_BLOCK_EXISTS_RES); - const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString()); + const result = await ethImpl.getBlockTransactionCountByNumber(BLOCK_NUMBER.toString(), requestDetails); expect(result).to.equal(null); }); @@ -91,7 +96,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('latest'); + const result = await ethImpl.getBlockTransactionCountByNumber('latest', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -100,7 +105,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('finalized'); + const result = await ethImpl.getBlockTransactionCountByNumber('finalized', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -109,7 +114,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('safe'); + const result = await ethImpl.getBlockTransactionCountByNumber('safe', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -118,7 +123,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('pending'); + const result = await ethImpl.getBlockTransactionCountByNumber('pending', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -126,7 +131,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function // mirror node request mocks restMock.onGet(`blocks/0`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('earliest'); + const result = await ethImpl.getBlockTransactionCountByNumber('earliest', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); @@ -134,7 +139,7 @@ describe('@ethGetBlockTransactionCountByNumber using MirrorNode', async function // mirror node request mocks restMock.onGet(`blocks/3735929054`).reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getBlockTransactionCountByNumber('0xdeadc0de'); + const result = await ethImpl.getBlockTransactionCountByNumber('0xdeadc0de', requestDetails); expect(result).equal(numberTo0x(BLOCK_TRANSACTION_COUNT)); }); }); diff --git a/packages/relay/tests/lib/eth/eth_getCode.spec.ts b/packages/relay/tests/lib/eth/eth_getCode.spec.ts index e475dcea8f..c5cd107218 100644 --- a/packages/relay/tests/lib/eth/eth_getCode.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getCode.spec.ts @@ -38,12 +38,13 @@ import { } from './eth-config'; import { generateEthTestEnv } from './eth-helpers'; import { JsonRpcError, predefined } from '../../../src'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGetCode using MirrorNode', async function () { @@ -52,9 +53,11 @@ describe('@ethGetCode using MirrorNode', async function () { let validBlockParam = [null, 'earliest', 'latest', 'pending', 'finalized', 'safe', '0x0', '0x369ABF']; let invalidBlockParam = ['hedera', 'ethereum', '0xhbar', '0x369ABF369ABF369ABF369ABF']; - this.beforeEach(() => { + const requestDetails = new RequestDetails({ requestId: 'eth_getCodeTest', ipAddress: '0.0.0.0' }); + + this.beforeEach(async () => { // reset cache and restMock - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -66,7 +69,7 @@ describe('@ethGetCode using MirrorNode', async function () { restMock.onGet(`accounts/${CONTRACT_ADDRESS_1}?limit=100`).reply(404, null); restMock.onGet(`tokens/0.0.${parseInt(CONTRACT_ADDRESS_1, 16)}`).reply(404, null); restMock.onGet(`contracts/${CONTRACT_ADDRESS_1}`).reply(200, DEFAULT_CONTRACT); - sdkClientStub.getContractByteCode.returns(Buffer.from(DEPLOYED_BYTECODE.replace('0x', ''), 'hex')); + sdkClientStub.getContractByteCode.resolves(Buffer.from(DEPLOYED_BYTECODE.replace('0x', ''), 'hex')); }); this.afterEach(() => { @@ -86,20 +89,20 @@ describe('@ethGetCode using MirrorNode', async function () { }), ); - const resNoCache = await ethImpl.getCode(CONTRACT_ADDRESS_1, null); - const resCached = await ethImpl.getCode(CONTRACT_ADDRESS_1, null); + const resNoCache = await ethImpl.getCode(CONTRACT_ADDRESS_1, null, requestDetails); + const resCached = await ethImpl.getCode(CONTRACT_ADDRESS_1, null, requestDetails); expect(resNoCache).to.equal(EthImpl.emptyHex); expect(resCached).to.equal(EthImpl.emptyHex); }); it('should return the runtime_bytecode from the mirror node', async () => { - const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null); + const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null, requestDetails); expect(res).to.equal(MIRROR_NODE_DEPLOYED_BYTECODE); }); it('should return the bytecode from SDK if Mirror Node returns 404', async () => { restMock.onGet(`contracts/${CONTRACT_ADDRESS_1}`).reply(404, DEFAULT_CONTRACT); - const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null); + const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null, requestDetails); expect(res).to.equal(DEPLOYED_BYTECODE); }); @@ -108,7 +111,7 @@ describe('@ethGetCode using MirrorNode', async function () { ...DEFAULT_CONTRACT, runtime_bytecode: EthImpl.emptyHex, }); - const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null); + const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, null, requestDetails); expect(res).to.equal(DEPLOYED_BYTECODE); }); @@ -119,7 +122,7 @@ describe('@ethGetCode using MirrorNode', async function () { const redirectBytecode = `6080604052348015600f57600080fd5b506000610167905077618dc65e${HTS_TOKEN_ADDRESS.slice( 2, )}600052366000602037600080366018016008845af43d806000803e8160008114605857816000f35b816000fdfea2646970667358221220d8378feed472ba49a0005514ef7087017f707b45fb9bf56bb81bb93ff19a238b64736f6c634300080b0033`; - const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, null); + const res = await ethImpl.getCode(HTS_TOKEN_ADDRESS, null, requestDetails); expect(res).to.equal(redirectBytecode); }); @@ -127,13 +130,13 @@ describe('@ethGetCode using MirrorNode', async function () { restMock.onGet(`contracts/${EthImpl.iHTSAddress}`).reply(200, DEFAULT_CONTRACT); restMock.onGet(`accounts/${EthImpl.iHTSAddress}${NO_TRANSACTIONS}`).reply(404, null); - const res = await ethImpl.getCode(EthImpl.iHTSAddress, null); + const res = await ethImpl.getCode(EthImpl.iHTSAddress, null, requestDetails); expect(res).to.equal(EthImpl.invalidEVMInstruction); }); validBlockParam.forEach((blockParam) => { it(`should pass the validate param check with blockParam=${blockParam} and return the bytecode`, async () => { - const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, blockParam); + const res = await ethImpl.getCode(CONTRACT_ADDRESS_1, blockParam, requestDetails); expect(res).to.equal(MIRROR_NODE_DEPLOYED_BYTECODE); }); }); @@ -141,7 +144,7 @@ describe('@ethGetCode using MirrorNode', async function () { invalidBlockParam.forEach((blockParam) => { it(`should throw INVALID_PARAMETER JsonRpcError with invalid blockParam=${blockParam}`, async () => { try { - await ethImpl.getCode(EthImpl.iHTSAddress, blockParam); + await ethImpl.getCode(EthImpl.iHTSAddress, blockParam, requestDetails); expect(true).to.eq(false); } catch (error: any) { const expectedError = predefined.UNKNOWN_BLOCK( diff --git a/packages/relay/tests/lib/eth/eth_getLogs.spec.ts b/packages/relay/tests/lib/eth/eth_getLogs.spec.ts index 27746b29e6..d2cd1e264c 100644 --- a/packages/relay/tests/lib/eth/eth_getLogs.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getLogs.spec.ts @@ -58,12 +58,17 @@ import { } from './eth-config'; import { ethers } from 'ethers'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { Eth } from '../../../src'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; dotenv.config({ path: path.resolve(__dirname, '../../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGetLogs using MirrorNode', async function () { @@ -76,14 +81,22 @@ describe('@ethGetLogs using MirrorNode', async function () { to: '1651560395.060890949', }, }; - let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + }: { restMock: MockAdapter; hapiServiceInstance: HAPIService; ethImpl: Eth; cacheService: CacheService } = + generateEthTestEnv(); const filteredLogs = { logs: [DEFAULT_LOGS.logs[0], DEFAULT_LOGS.logs[1]], }; - this.beforeEach(() => { + const requestDetails = new RequestDetails({ requestId: 'eth_getLogsTest', ipAddress: '0.0.0.0' }); + + beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -93,13 +106,13 @@ describe('@ethGetLogs using MirrorNode', async function () { process.env.ETH_GET_TRANSACTION_COUNT_MAX_BLOCK_RANGE = '1'; }); - this.afterEach(() => { + afterEach(() => { getSdkClientStub.restore(); process.env.ETH_GET_TRANSACTION_COUNT_MAX_BLOCK_RANGE = currentMaxBlockRange.toString(); }); describe('timeout', async function () { - this.beforeEach(() => { + beforeEach(() => { restMock.onGet(`blocks/${BLOCK_HASH}`).timeout(); restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, DEFAULT_BLOCKS_RES); restMock.onGet(CONTRACTS_LOGS_WITH_FILTER).timeout(); @@ -107,14 +120,14 @@ describe('@ethGetLogs using MirrorNode', async function () { }); it('BLOCK_HASH filter timeouts and throws the expected error', async () => { - await ethGetLogsFailing(ethImpl, [BLOCK_HASH, null, null, null, null], (error) => { + await ethGetLogsFailing(ethImpl, [BLOCK_HASH, null, null, null, null, requestDetails], (error: any) => { expect(error.statusCode).to.equal(504); expect(error.message).to.eq('timeout of 10000ms exceeded'); }); }); it('address filter timeouts and throws the expected error', async () => { - await ethGetLogsFailing(ethImpl, [null, null, null, CONTRACT_ADDRESS_1, null], (error) => { + await ethGetLogsFailing(ethImpl, [null, null, null, CONTRACT_ADDRESS_1, null, requestDetails], (error: any) => { expect(error.statusCode).to.equal(504); expect(error.message).to.eq('timeout of 10000ms exceeded'); }); @@ -129,8 +142,8 @@ describe('@ethGetLogs using MirrorNode', async function () { let errorReceived = false; try { - await ethImpl.getLogs(null, null, null, null, null); - } catch (error) { + await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); + } catch (error: any) { errorReceived = true; expect(error.statusCode).to.equal(400); expect(error.message).to.eq('Mocked error'); @@ -154,7 +167,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, { ...DEFAULT_CONTRACT, contract_id: `0.0.105${index}` }); }); - const result = await ethImpl.getLogs(null, null, null, null, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); expect(result).to.exist; expect(result.length).to.eq(4); @@ -179,11 +192,11 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, { ...DEFAULT_CONTRACT, contract_id: `0.0.105${index}` }); }); - const result = await ethImpl.getLogs(null, null, null, null, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); expect(result).to.exist; expect(result.length).to.eq(4); - result.forEach((log, index) => { + result.forEach((log, _index) => { expect(log.transactionIndex).to.be.null; }); }); @@ -227,7 +240,7 @@ describe('@ethGetLogs using MirrorNode', async function () { }); //setting mirror node limit to 2 for this test only process.env['MIRROR_NODE_LIMIT_PARAM'] = '2'; - const result = await ethImpl.getLogs(null, null, null, null, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); //resetting mirror node limit to 100 process.env['MIRROR_NODE_LIMIT_PARAM'] = '100'; expect(result).to.exist; @@ -255,7 +268,7 @@ describe('@ethGetLogs using MirrorNode', async function () { .onGet(`contracts/${filteredLogs.logs[0].address}`) .reply(200, { ...DEFAULT_CONTRACT, evm_address: defaultEvmAddress }); - const result = await ethImpl.getLogs(null, null, null, null, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); expect(result).to.exist; expect(result.length).to.eq(1); @@ -272,7 +285,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, null, null, CONTRACT_ADDRESS_1, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', CONTRACT_ADDRESS_1, null, requestDetails); expect(result).to.exist; @@ -310,7 +323,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet('blocks/1').reply(200, fromBlock); restMock.onGet('blocks/1003').reply(200, toBlock); - const result = await ethImpl.getLogs(null, '0x1', '0x3eb', address, null); + const result = await ethImpl.getLogs(null, '0x1', '0x3eb', address, null, requestDetails); expect(result).to.exist; @@ -340,7 +353,14 @@ describe('@ethGetLogs using MirrorNode', async function () { } restMock.onGet(`contracts/${CONTRACT_ADDRESS_2}`).reply(200, DEFAULT_CONTRACT_2); - const result = await ethImpl.getLogs(null, null, null, [CONTRACT_ADDRESS_1, CONTRACT_ADDRESS_2], null); + const result = await ethImpl.getLogs( + null, + 'latest', + 'latest', + [CONTRACT_ADDRESS_1, CONTRACT_ADDRESS_2], + null, + requestDetails, + ); expect(result).to.exist; @@ -362,7 +382,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(BLOCK_HASH, null, null, null, null); + const result = await ethImpl.getLogs(BLOCK_HASH, 'latest', 'latest', null, null, requestDetails); expect(result).to.exist; expectLogData1(result[0]); @@ -394,7 +414,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, '0x5', '0x10', null, null); + const result = await ethImpl.getLogs(null, '0x5', '0x10', null, null, requestDetails); expect(result).to.exist; expectLogData1(result[0]); @@ -407,7 +427,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet('blocks/5').reply(200, DEFAULT_BLOCK); restMock.onGet('blocks/16').reply(404, NOT_FOUND_RES); - const result = await ethImpl.getLogs(null, '0x10', '0x5', null, null); + const result = await ethImpl.getLogs(null, '0x10', '0x5', null, null, requestDetails); expect(result).to.exist; expect(result).to.be.empty; @@ -426,7 +446,7 @@ describe('@ethGetLogs using MirrorNode', async function () { .reply(200, filteredLogs); restMock.onGet(`contracts/${filteredLogs.logs[0].address}`).reply(200, DEFAULT_CONTRACT); - const result = await ethImpl.getLogs(null, '0x5', '0x10', null, null); + const result = await ethImpl.getLogs(null, '0x5', '0x10', null, null, requestDetails); expect(result).to.exist; expectLogData1(result[0]); @@ -445,7 +465,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [latestBlock] }); restMock.onGet('blocks/16').reply(200, fromBlock); restMock.onGet('blocks/5').reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getLogs(null, '0x10', '0x5', null, null); + const result = await ethImpl.getLogs(null, '0x10', '0x5', null, null, requestDetails); expect(result).to.exist; expect(result).to.be.empty; @@ -455,7 +475,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [latestBlock] }); restMock.onGet('blocks/5').reply(200, DEFAULT_BLOCKS_RES); - await ethGetLogsFailing(ethImpl, [null, null, '0x5', null, null], (error) => { + await ethGetLogsFailing(ethImpl, [null, null, '0x5', null, null, requestDetails], (error: any) => { expect(error.code).to.equal(-32011); expect(error.message).to.equal('Provided toBlock parameter without specifying fromBlock'); }); @@ -472,7 +492,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, 'latest', null, null, null); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); expect(result).to.exist; expectLogData1(result[0]); @@ -498,7 +518,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet('blocks/1').reply(200, fromBlock); restMock.onGet('blocks/1003').reply(200, toBlock); - await ethGetLogsFailing(ethImpl, [null, '0x1', '0x3eb', address, null], (error) => { + await ethGetLogsFailing(ethImpl, [null, '0x1', '0x3eb', address, null, requestDetails], (error: any) => { expect(error.message).to.equal('Exceeded maximum block range: 1000'); }); }); @@ -523,7 +543,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, null, null, null, DEFAULT_LOG_TOPICS); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, DEFAULT_LOG_TOPICS, requestDetails); expect(result).to.exist; expectLogData1(result[0]); @@ -547,7 +567,7 @@ describe('@ethGetLogs using MirrorNode', async function () { for (const log of filteredLogs.logs) { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, null, null, null, DEFAULT_NULL_LOG_TOPICS); + const result = await ethImpl.getLogs(null, 'latest', 'latest', null, DEFAULT_NULL_LOG_TOPICS, requestDetails); expect(result).to.exist; expect(result[0].topics.length).to.eq(DEFAULT_LOGS_4[0].topics.length); @@ -577,7 +597,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT); } - const result = await ethImpl.getLogs(null, '0x5', '0x10', null, DEFAULT_LOG_TOPICS); + const result = await ethImpl.getLogs(null, '0x5', '0x10', null, DEFAULT_LOG_TOPICS, requestDetails); expectLogData1(result[0]); expectLogData2(result[1]); @@ -587,7 +607,7 @@ describe('@ethGetLogs using MirrorNode', async function () { restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [latestBlock] }); restMock.onGet('blocks/0').reply(200, DEFAULT_BLOCK); restMock.onGet('blocks/latest').reply(200, DEFAULT_BLOCK); - const result = await ethImpl.getLogs(null, '0x0', 'latest', ethers.ZeroAddress, DEFAULT_LOG_TOPICS); + const result = await ethImpl.getLogs(null, '0x0', 'latest', ethers.ZeroAddress, DEFAULT_LOG_TOPICS, requestDetails); expect(result.length).to.eq(0); expect(result).to.deep.equal([]); }); diff --git a/packages/relay/tests/lib/eth/eth_getStorageAt.spec.ts b/packages/relay/tests/lib/eth/eth_getStorageAt.spec.ts index 9c552380e7..4b5d5033b3 100644 --- a/packages/relay/tests/lib/eth/eth_getStorageAt.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getStorageAt.spec.ts @@ -40,23 +40,33 @@ import { OLDER_BLOCK, BLOCK_HASH, } from './eth-config'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { Eth, predefined } from '../../../src'; import RelayAssertions from '../../assertions'; import { defaultDetailedContractResults } from '../../helpers'; import { numberTo0x } from '../../../src/formatters'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGetStorageAt eth_getStorageAt spec', async function () { this.timeout(10000); - let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); - + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + }: { restMock: MockAdapter; hapiServiceInstance: HAPIService; ethImpl: Eth; cacheService: CacheService } = + generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'eth_getStorageAtTest', ipAddress: '0.0.0.0' }); function confirmResult(result: string) { expect(result).to.exist; expect(result).to.not.be.null; @@ -65,7 +75,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -93,7 +103,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { ) .reply(200, DEFAULT_CURRENT_CONTRACT_STATE); - const result = await ethImpl.getStorageAt(CONTRACT_ADDRESS_1, '0x101', numberTo0x(BLOCK_NUMBER)); + const result = await ethImpl.getStorageAt(CONTRACT_ADDRESS_1, '0x101', requestDetails, numberTo0x(BLOCK_NUMBER)); confirmResult(result); // verify slot value @@ -108,7 +118,12 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { ) .reply(200, DEFAULT_CURRENT_CONTRACT_STATE); - const result = await ethImpl.getStorageAt(CONTRACT_ADDRESS_1, '0x0000101', numberTo0x(BLOCK_NUMBER)); + const result = await ethImpl.getStorageAt( + CONTRACT_ADDRESS_1, + '0x0000101', + requestDetails, + numberTo0x(BLOCK_NUMBER), + ); confirmResult(result); // verify slot value @@ -126,6 +141,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, defaultDetailedContractResults.state_changes[0].slot, + requestDetails, numberTo0x(BLOCK_NUMBER), ); confirmResult(result); @@ -145,6 +161,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, defaultDetailedContractResults.state_changes[0].slot, + requestDetails, BLOCK_HASH, ); confirmResult(result); @@ -164,6 +181,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, DEFAULT_CURRENT_CONTRACT_STATE.state[0].slot, + requestDetails, 'latest', ); confirmResult(result); @@ -182,6 +200,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, DEFAULT_CURRENT_CONTRACT_STATE.state[0].slot, + requestDetails, 'finalized', ); confirmResult(result); @@ -200,6 +219,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, DEFAULT_CURRENT_CONTRACT_STATE.state[0].slot, + requestDetails, 'safe', ); confirmResult(result); @@ -208,7 +228,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { }); // Block number is a required param, this should not work and should be removed when/if validations are added. - // Instead the relay should return `missing value for required argument error`. + // Instead, the relay should return `missing value for required argument error`. it('eth_getStorageAt with match null block', async function () { // mirror node request mocks restMock @@ -220,6 +240,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, defaultDetailedContractResults.state_changes[0].slot, + requestDetails, ); confirmResult(result); @@ -229,7 +250,12 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { it('eth_getStorageAt should throw a predefined RESOURCE_NOT_FOUND when block not found', async function () { restMock.onGet(`blocks/${BLOCK_NUMBER}`).reply(200, null); - const args = [CONTRACT_ADDRESS_1, defaultDetailedContractResults.state_changes[0].slot, numberTo0x(BLOCK_NUMBER)]; + const args = [ + CONTRACT_ADDRESS_1, + defaultDetailedContractResults.state_changes[0].slot, + requestDetails, + numberTo0x(BLOCK_NUMBER), + ]; await RelayAssertions.assertRejection( predefined.RESOURCE_NOT_FOUND(), @@ -248,7 +274,12 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { ) .reply(200, DEFAULT_CONTRACT_STATE_EMPTY_ARRAY); - const result = await ethImpl.getStorageAt(CONTRACT_ADDRESS_1, wrongSlot, numberTo0x(BLOCK_NUMBER)); + const result = await ethImpl.getStorageAt( + CONTRACT_ADDRESS_1, + wrongSlot, + requestDetails, + numberTo0x(BLOCK_NUMBER), + ); expect(result).to.equal(EthImpl.zeroHex32Byte); }); @@ -263,6 +294,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, DEFAULT_OLDER_CONTRACT_STATE.state[0].slot, + requestDetails, numberTo0x(OLDER_BLOCK.number), ); expect(result).to.equal(DEFAULT_OLDER_CONTRACT_STATE.state[0].value); @@ -279,6 +311,7 @@ describe('@ethGetStorageAt eth_getStorageAt spec', async function () { const result = await ethImpl.getStorageAt( CONTRACT_ADDRESS_1, DEFAULT_OLDER_CONTRACT_STATE.state[0].slot, + requestDetails, numberTo0x(OLDER_BLOCK.number), ); expect(result).to.equal(ethers.ZeroHash); diff --git a/packages/relay/tests/lib/eth/eth_getTransactionByBlockHashAndIndex.spec.ts b/packages/relay/tests/lib/eth/eth_getTransactionByBlockHashAndIndex.spec.ts index 2b071c1194..cfb59c429f 100644 --- a/packages/relay/tests/lib/eth/eth_getTransactionByBlockHashAndIndex.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getTransactionByBlockHashAndIndex.spec.ts @@ -21,10 +21,10 @@ import path from 'path'; import dotenv from 'dotenv'; import { expect, use } from 'chai'; import sinon from 'sinon'; -import * as _ from 'lodash'; +import _ from 'lodash'; import chaiAsPromised from 'chai-as-promised'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { Eth, predefined } from '../../../src'; import { defaultContractResults, defaultDetailedContractResults } from '../../helpers'; import { Transaction, Transaction1559, Transaction2930 } from '../../../src/lib/model'; import { SDKClient } from '../../../src/lib/clients'; @@ -41,17 +41,18 @@ import { DEFAULT_NETWORK_FEES, EMPTY_RES, NOT_FOUND_RES, - ACCOUNT_ADDRESS_1, - CONTRACT_ADDRESS_2, - CONTRACT_ID_2, } from './eth-config'; import { contractResultsByHashByIndexURL, generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; function verifyAggregatedInfo(result: Transaction | null) { // verify aggregated info @@ -65,11 +66,22 @@ function verifyAggregatedInfo(result: Transaction | null) { describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async function () { this.timeout(10000); - let { restMock, web3Mock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + }: { restMock: MockAdapter; hapiServiceInstance: HAPIService; ethImpl: Eth; cacheService: CacheService } = + generateEthTestEnv(); + + const requestDetails = new RequestDetails({ + requestId: 'eth_getTransactionByBlockHashAndIndexTest', + ipAddress: '0.0.0.0', + }); - this.beforeEach(() => { + this.beforeEach(async () => { // reset cache and restMock - cacheService.clear(); + await cacheService.clear(requestDetails); restMock.reset(); sdkClientStub = sinon.createStubInstance(SDKClient); @@ -100,7 +112,11 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio restMock .onGet(`contracts/${CONTRACT_ADDRESS_1}/results/${CONTRACT_TIMESTAMP_1}`) .reply(200, defaultDetailedContractResults); - const result = await ethImpl.getTransactionByBlockHashAndIndex(DEFAULT_BLOCK.hash, numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockHashAndIndex( + DEFAULT_BLOCK.hash, + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); expect(result).to.exist; expect(result).to.not.be.null; @@ -118,7 +134,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio .onGet(contractResultsByHashByIndexURL(randomBlock.hash, randomBlock.count)) .reply(200, defaultContractResultsWithNullableFrom); - const args = [randomBlock.hash, numberTo0x(randomBlock.count)]; + const args = [randomBlock.hash, numberTo0x(randomBlock.count), requestDetails]; const errMessage = "Cannot read properties of null (reading 'substring')"; await RelayAssertions.assertRejection( @@ -137,6 +153,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio const result = await ethImpl.getTransactionByBlockHashAndIndex( DEFAULT_BLOCK.hash.toString(), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.equal(null); }); @@ -147,6 +164,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio const result = await ethImpl.getTransactionByBlockHashAndIndex( DEFAULT_BLOCK.hash.toString(), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.equal(null); }); @@ -164,6 +182,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio const result = await ethImpl.getTransactionByBlockHashAndIndex( DEFAULT_BLOCK.hash.toString(), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.be.an.instanceOf(Transaction); }); @@ -182,6 +201,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio const result = await ethImpl.getTransactionByBlockHashAndIndex( DEFAULT_BLOCK.hash.toString(), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.be.an.instanceOf(Transaction2930); }); @@ -202,6 +222,7 @@ describe('@ethGetTransactionByBlockHashAndIndex using MirrorNode', async functio const result = await ethImpl.getTransactionByBlockHashAndIndex( DEFAULT_BLOCK.hash.toString(), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.be.an.instanceOf(Transaction1559); }); diff --git a/packages/relay/tests/lib/eth/eth_getTransactionByBlockNumberAndIndex.spec.ts b/packages/relay/tests/lib/eth/eth_getTransactionByBlockNumberAndIndex.spec.ts index 6578717e47..82c274e719 100644 --- a/packages/relay/tests/lib/eth/eth_getTransactionByBlockNumberAndIndex.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getTransactionByBlockNumberAndIndex.spec.ts @@ -24,7 +24,7 @@ import sinon from 'sinon'; import * as _ from 'lodash'; import chaiAsPromised from 'chai-as-promised'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { Eth, predefined } from '../../../src'; import { defaultContractResults, defaultDetailedContractResults } from '../../helpers'; import { Transaction } from '../../../src/lib/model'; import { SDKClient } from '../../../src/lib/clients'; @@ -43,12 +43,16 @@ import { NOT_FOUND_RES, } from './eth-config'; import { contractResultsByNumberByIndexURL, generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; function verifyAggregatedInfo(result: Transaction | null) { // verify aggregated info @@ -64,13 +68,23 @@ function verifyAggregatedInfo(result: Transaction | null) { describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async function () { this.timeout(10000); - let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + }: { restMock: MockAdapter; hapiServiceInstance: HAPIService; ethImpl: Eth; cacheService: CacheService } = + generateEthTestEnv(); + + const requestDetails = new RequestDetails({ + requestId: 'eth_getTransactionByBlockNumberAndIndexTest', + ipAddress: '0.0.0.0', + }); this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -103,6 +117,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct const result = await ethImpl.getTransactionByBlockNumberAndIndex( numberTo0x(DEFAULT_BLOCK.number), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); verifyAggregatedInfo(result); @@ -123,6 +138,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct const result = await ethImpl.getTransactionByBlockNumberAndIndex( numberTo0x(randomBlock.number), numberTo0x(randomBlock.count), + requestDetails, ); expect(result).to.exist; expect(result).to.not.be.null; @@ -141,6 +157,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct const result = await ethImpl.getTransactionByBlockNumberAndIndex( numberTo0x(DEFAULT_BLOCK.number), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.equal(null); }); @@ -156,7 +173,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct .onGet(contractResultsByNumberByIndexURL(randomBlock.number, randomBlock.count)) .reply(200, defaultContractResultsWithNullableFrom); - const args = [numberTo0x(randomBlock.number), numberTo0x(randomBlock.count)]; + const args = [numberTo0x(randomBlock.number), numberTo0x(randomBlock.count), requestDetails]; const errMessage = "Cannot read properties of null (reading 'substring')"; await RelayAssertions.assertRejection( @@ -176,6 +193,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct const result = await ethImpl.getTransactionByBlockNumberAndIndex( numberTo0x(DEFAULT_BLOCK.number), numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); expect(result).to.equal(null); }); @@ -187,7 +205,11 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct .onGet(contractResultsByNumberByIndexURL(DEFAULT_BLOCK.number, DEFAULT_BLOCK.count)) .reply(200, defaultContractResults); - const result = await ethImpl.getTransactionByBlockNumberAndIndex('latest', numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockNumberAndIndex( + 'latest', + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); verifyAggregatedInfo(result); }); @@ -198,7 +220,11 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct .onGet(contractResultsByNumberByIndexURL(DEFAULT_BLOCK.number, DEFAULT_BLOCK.count)) .reply(200, defaultContractResults); - const result = await ethImpl.getTransactionByBlockNumberAndIndex('finalized', numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockNumberAndIndex( + 'finalized', + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); verifyAggregatedInfo(result); }); @@ -209,7 +235,11 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct .onGet(contractResultsByNumberByIndexURL(DEFAULT_BLOCK.number, DEFAULT_BLOCK.count)) .reply(200, defaultContractResults); - const result = await ethImpl.getTransactionByBlockNumberAndIndex('safe', numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockNumberAndIndex( + 'safe', + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); verifyAggregatedInfo(result); }); @@ -220,7 +250,11 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct .onGet(contractResultsByNumberByIndexURL(DEFAULT_BLOCK.number, DEFAULT_BLOCK.count)) .reply(200, defaultContractResults); - const result = await ethImpl.getTransactionByBlockNumberAndIndex('pending', numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockNumberAndIndex( + 'pending', + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); verifyAggregatedInfo(result); }); @@ -228,7 +262,11 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct // mirror node request mocks restMock.onGet(contractResultsByNumberByIndexURL(0, DEFAULT_BLOCK.count)).reply(200, defaultContractResults); - const result = await ethImpl.getTransactionByBlockNumberAndIndex('earliest', numberTo0x(DEFAULT_BLOCK.count)); + const result = await ethImpl.getTransactionByBlockNumberAndIndex( + 'earliest', + numberTo0x(DEFAULT_BLOCK.count), + requestDetails, + ); verifyAggregatedInfo(result); }); @@ -240,6 +278,7 @@ describe('@ethGetTransactionByBlockNumberAndIndex using MirrorNode', async funct const result = await ethImpl.getTransactionByBlockNumberAndIndex( '0xdeadc0de' + '', numberTo0x(DEFAULT_BLOCK.count), + requestDetails, ); verifyAggregatedInfo(result); }); diff --git a/packages/relay/tests/lib/eth/eth_getTransactionByHash.spec.ts b/packages/relay/tests/lib/eth/eth_getTransactionByHash.spec.ts index 4b4106a225..3b3b93d942 100644 --- a/packages/relay/tests/lib/eth/eth_getTransactionByHash.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getTransactionByHash.spec.ts @@ -22,11 +22,8 @@ import dotenv from 'dotenv'; import { expect, use } from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import { EthImpl } from '../../../src/lib/eth'; -import { Log, Transaction, Transaction2930, Transaction1559 } from '../../../src/lib/model'; -import constants from '../../../src/lib/constants'; +import { Transaction, Transaction2930, Transaction1559 } from '../../../src/lib/model'; import RelayAssertions from '../../assertions'; -import { nullableNumberTo0x, numberTo0x, toHash32 } from '../../../src/formatters'; import { DEFAULT_DETAILED_CONTRACT_RESULT_BY_HASH_REVERTED, DEFAULT_TRANSACTION, @@ -35,15 +32,15 @@ import { EMPTY_LOGS_RESPONSE, NO_TRANSACTIONS, } from './eth-config'; -import { defaultDetailedContractResultByHash, defaultFromLongZeroAddress, defaultLogs1 } from '../../helpers'; -import { predefined } from '../../../src'; +import { defaultDetailedContractResultByHash, defaultFromLongZeroAddress } from '../../helpers'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async function () { - let { restMock, ethImpl, cacheService } = generateEthTestEnv(); + let { restMock, ethImpl } = generateEthTestEnv(); const from = '0x00000000000000000000000000000000000003f7'; const evm_address = '0xc37f417fa09933335240fca72dd257bfbde9c275'; const contractResultMock = { @@ -79,6 +76,8 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi nonce: 9, }; + const requestDetails = new RequestDetails({ requestId: 'eth_getTransactionByHashTest', ipAddress: '0.0.0.0' }); + this.beforeEach(function () { restMock.reset(); restMock.onGet(`accounts/${defaultFromLongZeroAddress}${NO_TRANSACTIONS}`).reply(200, { @@ -96,7 +95,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi type: 0, }); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.be.an.instanceOf(Transaction); }); @@ -108,7 +107,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi access_list: [], }); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.be.an.instanceOf(Transaction2930); }); @@ -122,7 +121,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi max_priority_fee_per_gas: '0x47', }); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.be.an.instanceOf(Transaction1559); }); @@ -133,21 +132,21 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi .onGet(`contracts/results/logs?transaction.hash=${uniqueTxHash}&limit=100&order=asc`) .reply(200, EMPTY_LOGS_RESPONSE); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.equal(null); }); it('account should be cached', async function () { restMock.onGet(`contracts/results/${DEFAULT_TX_HASH}`).reply(200, defaultDetailedContractResultByHash); - const resBeforeCache = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH); + const resBeforeCache = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH, requestDetails); restMock.onGet(`accounts/${defaultFromLongZeroAddress}${NO_TRANSACTIONS}`).reply(404); - const resAfterCache = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH); + const resAfterCache = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH, requestDetails); expect(resBeforeCache).to.deep.equal(resAfterCache); }); it('returns correct transaction for existing hash', async function () { restMock.onGet(`contracts/results/${DEFAULT_TX_HASH}`).reply(200, defaultDetailedContractResultByHash); - const result = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH); + const result = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH, requestDetails); RelayAssertions.assertTransaction(result, { ...DEFAULT_TRANSACTION, maxFeePerGas: '0x55', @@ -165,7 +164,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x97cad7b827375d12d73af57b6a3f84353645fd31305ea58ff52dda53ec640533'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithZeroXZeroValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); RelayAssertions.assertTransaction(result, { ...DEFAULT_TRANSACTION, maxFeePerGas: '0x55', @@ -183,7 +182,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x14aad7b827375d12d73af57b6a3e84353645fd31305ea58ff52dda53ec640533'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -198,7 +197,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x0aaad7b827375d12d73af57b6a3e84353645fd31305ea58ff52dda53ec640533'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -214,7 +213,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0xb4cad7b827375d12d73af57b6a3e84353645fd31305ea58ff52dda53ec640533'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -229,7 +228,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x14aad7b827375d12d73af57b6a3e84353645fd31305ea58ff52dda53ec640534'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -244,7 +243,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x14aad7b827375d12d73af57b6a3e84353645fd31305ea58ff52dda53ec640511'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -261,7 +260,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi const uniqueTxHash = '0x14aad7b827375d12d73af57b6a3e84353645fd31305ea58ff52d1a53ec640511'; restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, detailedResultsWithNullNullableValues); - const result = await ethImpl.getTransactionByHash(uniqueTxHash); + const result = await ethImpl.getTransactionByHash(uniqueTxHash, requestDetails); expect(result).to.not.be.null; expect(result).to.exist; @@ -276,7 +275,7 @@ describe('@ethGetTransactionByHash eth_getTransactionByHash tests', async functi .onGet(`contracts/results/${DEFAULT_TX_HASH}`) .reply(200, DEFAULT_DETAILED_CONTRACT_RESULT_BY_HASH_REVERTED); - const result = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH); + const result = await ethImpl.getTransactionByHash(DEFAULT_TX_HASH, requestDetails); RelayAssertions.assertTransaction(result, { ...DEFAULT_TRANSACTION, maxFeePerGas: '0x55', diff --git a/packages/relay/tests/lib/eth/eth_getTransactionCount.spec.ts b/packages/relay/tests/lib/eth/eth_getTransactionCount.spec.ts index 7687a92249..8beeda137a 100644 --- a/packages/relay/tests/lib/eth/eth_getTransactionCount.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getTransactionCount.spec.ts @@ -27,22 +27,34 @@ import { EthImpl } from '../../../src/lib/eth'; import constants from '../../../src/lib/constants'; import { SDKClient } from '../../../src/lib/clients'; import { DEFAULT_NETWORK_FEES, NO_TRANSACTIONS } from './eth-config'; -import { predefined } from '../../../src/lib/errors/JsonRpcError'; +import { Eth, predefined } from '../../../src'; import RelayAssertions from '../../assertions'; import { defaultDetailedContractResults, defaultEthereumTransactions, mockData } from '../../helpers'; import { numberTo0x } from '../../../src/formatters'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; +import MockAdapter from 'axios-mock-adapter'; +import HAPIService from '../../../src/lib/services/hapiService/hapiService'; +import { CacheService } from '../../../src/lib/services/cacheService/cacheService'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethGetTransactionCount eth_getTransactionCount spec', async function () { this.timeout(10000); - let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const { + restMock, + hapiServiceInstance, + ethImpl, + cacheService, + }: { restMock: MockAdapter; hapiServiceInstance: HAPIService; ethImpl: Eth; cacheService: CacheService } = + generateEthTestEnv(); + + const requestDetails = new RequestDetails({ requestId: 'eth_getTransactionCountTest', ipAddress: '0.0.0.0' }); const blockNumber = mockData.blocks.blocks[2].number; const blockNumberHex = numberTo0x(blockNumber); const transactionId = '0.0.1078@1686183420.196506746'; @@ -56,8 +68,8 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function const blockPath = `blocks/${blockNumber}`; const latestBlockPath = `blocks?limit=1&order=desc`; - function transactionPath(addresss, num) { - return `accounts/${addresss}?transactiontype=ETHEREUMTRANSACTION×tamp=lte:${mockData.blocks.blocks[2].timestamp.to}&limit=${num}&order=desc`; + function transactionPath(address: string, num: number) { + return `accounts/${address}?transactiontype=ETHEREUMTRANSACTION×tamp=lte:${mockData.blocks.blocks[2].timestamp.to}&limit=${num}&order=desc`; } this.beforeEach(() => { @@ -84,7 +96,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function restMock.resetHandlers(); process.env.ETH_GET_TRANSACTION_COUNT_MAX_BLOCK_RANGE = currentMaxBlockRange.toString(); // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); }); @@ -96,7 +108,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should return 0x0 nonce for no block consideration with not found acoount', async () => { restMock.onGet(contractPath).reply(404, mockData.notFound); restMock.onGet(accountPath).reply(404, mockData.notFound); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, null); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, null, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.zeroHex); }); @@ -104,63 +116,63 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should return latest nonce for no block consideration but valid account', async () => { restMock.onGet(contractPath).reply(404, mockData.notFound); restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, null); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, null, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should return 0x0 nonce for block 0 consideration', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, '0'); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, '0', requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.zeroHex); }); it('should return 0x0 nonce for block 1 consideration', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, '1'); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, '1', requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.zeroHex); }); it('should return latest nonce for latest block', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockLatest); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockLatest, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should return latest nonce for finalized block', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockFinalized); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockFinalized, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should return latest nonce for latest block', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockSafe); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockSafe, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should return latest nonce for pending block', async () => { restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockPending); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockPending, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should return 0x0 nonce for earliest block with valid block', async () => { restMock.onGet(earliestBlockPath).reply(200, { blocks: [mockData.blocks.blocks[0]] }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.zeroHex); }); it('should throw error for earliest block with invalid block', async () => { restMock.onGet(earliestBlockPath).reply(200, { blocks: [] }); - const args = [MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest]; + const args = [MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest, requestDetails]; await RelayAssertions.assertRejection( predefined.INTERNAL_ERROR('No network blocks found'), @@ -174,7 +186,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should throw error for earliest block with non 0 or 1 block', async () => { restMock.onGet(earliestBlockPath).reply(200, { blocks: [mockData.blocks.blocks[2]] }); - const args = [MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest]; + const args = [MOCK_ACCOUNT_ADDR, EthImpl.blockEarliest, requestDetails]; const errMessage = `Partial mirror node encountered, earliest block number is ${mockData.blocks.blocks[2].number}`; @@ -199,7 +211,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function .onGet(accountPathContractResultsAddress) .reply(200, { ...mockData.account, transactions: [defaultEthereumTransactions[0]] }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(`0x${defaultDetailedContractResults.nonce + 1}`); }); @@ -207,7 +219,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should throw error for account historical numerical block tag with missing block', async () => { restMock.onGet(blockPath).reply(404, mockData.notFound); - const args = [MOCK_ACCOUNT_ADDR, blockNumberHex]; + const args = [MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails]; await RelayAssertions.assertRejection(predefined.UNKNOWN_BLOCK(), ethImpl.getTransactionCount, true, ethImpl, args); }); @@ -216,7 +228,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function restMock.onGet(blockPath).reply(404, mockData.notFound); restMock.onGet(latestBlockPath).reply(404, mockData.notFound); - const args = [MOCK_ACCOUNT_ADDR, blockNumberHex]; + const args = [MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails]; await RelayAssertions.assertRejection(predefined.UNKNOWN_BLOCK(), ethImpl.getTransactionCount, true, ethImpl, args); }); @@ -232,7 +244,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function }); restMock.onGet(accountPath).reply(200, mockData.account); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); @@ -240,7 +252,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should return 0x0 nonce for historical numerical block with no ethereum transactions found', async () => { restMock.onGet(transactionPath(MOCK_ACCOUNT_ADDR, 2)).reply(200, { transactions: [] }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.zeroHex); }); @@ -248,7 +260,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function it('should return 0x1 nonce for historical numerical block with a single ethereum transactions found', async () => { restMock.onGet(transactionPath(MOCK_ACCOUNT_ADDR, 2)).reply(200, { transactions: [{}] }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.oneHex); }); @@ -259,7 +271,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function .reply(200, { transactions: [{ transaction_id: transactionId }, {}] }); restMock.onGet(contractResultsPath).reply(404, mockData.notFound); - const args = [MOCK_ACCOUNT_ADDR, blockNumberHex]; + const args = [MOCK_ACCOUNT_ADDR, blockNumberHex, requestDetails]; const errMessage = `Failed to retrieve contract results for transaction ${transactionId}`; await RelayAssertions.assertRejection( @@ -279,7 +291,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function .onGet(accountPathContractResultsAddress) .reply(200, { ...mockData.account, transactions: [defaultEthereumTransactions[0]] }); - const nonce = await ethImpl.getTransactionCount(mockData.account.evm_address, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(mockData.account.evm_address, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(3)); }); @@ -292,26 +304,26 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function restMock .onGet(accountPathContractResultsAddress) .reply(200, { ...mockData.account, transactions: [defaultEthereumTransactions[0]] }); - const nonce = await ethImpl.getTransactionCount(mockData.account.evm_address, blockNumberHex); + const nonce = await ethImpl.getTransactionCount(mockData.account.evm_address, blockNumberHex, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(mockData.account.ethereum_nonce)); }); it('should throw for -1 invalid block tag', async () => { - const args = [MOCK_ACCOUNT_ADDR, '-1']; + const args = [MOCK_ACCOUNT_ADDR, '-1', requestDetails]; await RelayAssertions.assertRejection(predefined.UNKNOWN_BLOCK(), ethImpl.getTransactionCount, true, ethImpl, args); }); it('should throw for invalid block tag', async () => { - const args = [MOCK_ACCOUNT_ADDR, 'notablock']; + const args = [MOCK_ACCOUNT_ADDR, 'notablock', requestDetails]; await RelayAssertions.assertRejection(predefined.UNKNOWN_BLOCK(), ethImpl.getTransactionCount, true, ethImpl, args); }); it('should return 0x1 for pre-hip-729 contracts with nonce=null', async () => { restMock.onGet(accountPath).reply(200, { ...mockData.account, ethereum_nonce: null }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockLatest); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, EthImpl.blockLatest, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(EthImpl.oneHex); }); @@ -325,7 +337,7 @@ describe('@ethGetTransactionCount eth_getTransactionCount spec', async function restMock .onGet(accountPathContractResultsAddress) .reply(200, { ...mockData.account, transactions: [defaultEthereumTransactions[0]] }); - const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockHash); + const nonce = await ethImpl.getTransactionCount(MOCK_ACCOUNT_ADDR, blockHash, requestDetails); expect(nonce).to.exist; expect(nonce).to.equal(numberTo0x(2)); }); diff --git a/packages/relay/tests/lib/eth/eth_getTransactionReceipt.spec.ts b/packages/relay/tests/lib/eth/eth_getTransactionReceipt.spec.ts index 3df66deddc..4676593171 100644 --- a/packages/relay/tests/lib/eth/eth_getTransactionReceipt.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getTransactionReceipt.spec.ts @@ -29,6 +29,7 @@ import RelayAssertions from '../../assertions'; import { DEFAULT_BLOCK, EMPTY_LOGS_RESPONSE } from './eth-config'; import { defaultErrorMessageHex } from '../../helpers'; import { generateEthTestEnv } from './eth-helpers'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); @@ -38,6 +39,8 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func let { restMock, ethImpl, cacheService } = generateEthTestEnv(); let sandbox: sinon.SinonSandbox; + const requestDetails = new RequestDetails({ requestId: 'eth_getTransactionReceiptTest', ipAddress: '0.0.0.0' }); + this.beforeAll(() => { // @ts-ignore sandbox = createSandbox(); @@ -140,7 +143,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func this.afterEach(() => { restMock.resetHandlers(); sandbox.restore(); - cacheService.clear(); + cacheService.clear(requestDetails); }); it('returns `null` for non-existent hash', async function () { @@ -157,7 +160,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock .onGet(`contracts/results/logs?transaction.hash=${txHash}&limit=100&order=asc`) .reply(200, EMPTY_LOGS_RESPONSE); - const receipt = await ethImpl.getTransactionReceipt(txHash); + const receipt = await ethImpl.getTransactionReceipt(txHash, requestDetails); expect(receipt).to.be.null; }); @@ -174,9 +177,9 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/results/${defaultTxHash}`).reply(200, defaultDetailedContractResultByHash); restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(defaultTxHash); + const receipt = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); - const currentGasPrice = await ethImpl.gasPrice('valid receipt on match TEST'); + const currentGasPrice = await ethImpl.gasPrice(requestDetails); // Assert the data format RelayAssertions.assertTransactionReceipt(receipt, defaultReceipt, { @@ -189,7 +192,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).replyOnce(404); stubBlockAndFeesFunc(sandbox); for (let i = 0; i < 3; i++) { - const receipt = await ethImpl.getTransactionReceipt(defaultTxHash); + const receipt = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); expect(receipt).to.exist; if (receipt == null) return; expect(RelayAssertions.validateHash(receipt.transactionHash, 64)).to.eq(true); @@ -207,7 +210,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func evm_address: contractEvmAddress, }); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(defaultTxHash); + const receipt = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); expect(receipt).to.exist; if (receipt == null) return; @@ -230,7 +233,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, contractResult); restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash); + const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash, requestDetails); expect(receipt).to.exist; if (receipt == null) return; @@ -247,7 +250,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/results/${defaultTxHash}`).reply(200, receiptWith0xBloom); restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(defaultTxHash); + const receipt = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); expect(receipt).to.exist; if (receipt == null) return; @@ -267,7 +270,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, receiptWithErrorMessage); restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash); + const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash, requestDetails); expect(receipt).to.exist; expect(receipt.revertReason).to.eq(defaultErrorMessageHex); @@ -284,7 +287,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func restMock.onGet(`contracts/results/${uniqueTxHash}`).reply(200, receiptWithNullGasUsed); restMock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash); + const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash, requestDetails); expect(receipt).to.exist; if (receipt == null) return; @@ -306,7 +309,7 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func evm_address: contractEvmAddress, }); stubBlockAndFeesFunc(sandbox); - const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash); + const receipt = await ethImpl.getTransactionReceipt(uniqueTxHash, requestDetails); expect(receipt).to.exist; @@ -332,10 +335,10 @@ describe('@ethGetTransactionReceipt eth_getTransactionReceipt tests', async func type: defaultDetailedContractResultByHash.type, }; - cacheService.set(cacheKey, cacheReceipt, EthImpl.ethGetTransactionReceipt); + await cacheService.set(cacheKey, cacheReceipt, EthImpl.ethGetTransactionReceipt, requestDetails); // w no mirror node requests - const receipt = await ethImpl.getTransactionReceipt(defaultTxHash); + const receipt = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); // Assert the matching reciept expect(receipt.blockHash).to.eq(cacheReceipt.blockHash); diff --git a/packages/relay/tests/lib/eth/eth_sendRawTransaction.spec.ts b/packages/relay/tests/lib/eth/eth_sendRawTransaction.spec.ts index 7a0ac92fdc..6b84daed42 100644 --- a/packages/relay/tests/lib/eth/eth_sendRawTransaction.spec.ts +++ b/packages/relay/tests/lib/eth/eth_sendRawTransaction.spec.ts @@ -22,32 +22,34 @@ import dotenv from 'dotenv'; import { expect, use } from 'chai'; import sinon from 'sinon'; import chaiAsPromised from 'chai-as-promised'; -import { Hbar, HbarUnit, TransactionId } from '@hashgraph/sdk'; +import { Hbar, HbarUnit, TransactionId, TransactionResponse } from '@hashgraph/sdk'; import { SDKClient } from '../../../src/lib/clients'; import { ACCOUNT_ADDRESS_1, DEFAULT_NETWORK_FEES, MAX_GAS_LIMIT_HEX, NO_TRANSACTIONS } from './eth-config'; -import { JsonRpcError, predefined } from '../../../src/lib/errors/JsonRpcError'; +import { JsonRpcError, predefined } from '../../../src'; import RelayAssertions from '../../assertions'; import { getRequestId, mockData, signTransaction } from '../../helpers'; import { generateEthTestEnv } from './eth-helpers'; import { SDKClientError } from '../../../src/lib/errors/SDKClientError'; +import { RequestDetails } from '../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); use(chaiAsPromised); -let sdkClientStub; -let getSdkClientStub; +let sdkClientStub: sinon.SinonStubbedInstance; +let getSdkClientStub: sinon.SinonStub; let currentMaxBlockRange: number; describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () { this.timeout(10000); let { restMock, hapiServiceInstance, ethImpl, cacheService } = generateEthTestEnv(); + const requestDetails = new RequestDetails({ requestId: 'testId', ipAddress: '0.0.0.0' }); + this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); - sdkClientStub = sinon.createStubInstance(SDKClient); getSdkClientStub = sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); @@ -93,7 +95,7 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () }, }; - this.beforeEach(() => { + beforeEach(() => { sinon.restore(); sdkClientStub = sinon.createStubInstance(SDKClient); sinon.stub(hapiServiceInstance, 'getSDKClient').returns(sdkClientStub); @@ -101,7 +103,7 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () restMock.onGet(networkExchangeRateEndpoint).reply(200, mockedExchangeRate); }); - this.afterEach(() => { + afterEach(() => { sinon.restore(); }); @@ -114,7 +116,7 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () ethImpl.sendRawTransaction, false, ethImpl, - [txHash, getRequestId()], + [txHash, requestDetails], ); }); @@ -123,7 +125,7 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () restMock.onGet(`transactions/${transactionId}`).reply(200, null); - const resultingHash = await ethImpl.sendRawTransaction(signed, getRequestId()); + const resultingHash = await ethImpl.sendRawTransaction(signed, requestDetails); expect(resultingHash).to.equal(ethereumHash); }); @@ -133,14 +135,14 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () restMock.onGet(contractResultEndpoint).reply(404, mockData.notFound); restMock.onGet(`transactions/${transactionId}?nonce=0`).reply(200, null); - sdkClientStub.submitEthereumTransaction.returns({ + sdkClientStub.submitEthereumTransaction.resolves({ txResponse: { transactionId: '', - }, + } as unknown as TransactionResponse, fileId: null, }); - const response = (await ethImpl.sendRawTransaction(signed, getRequestId())) as JsonRpcError; + const response = (await ethImpl.sendRawTransaction(signed, requestDetails)) as JsonRpcError; expect(response.code).to.equal(predefined.INTERNAL_ERROR().code); expect(`Error invoking RPC: ${response.message}`).to.equal(predefined.INTERNAL_ERROR(response.message).message); @@ -151,14 +153,14 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () restMock.onGet(contractResultEndpoint).reply(200, { hash: ethereumHash }); - sdkClientStub.submitEthereumTransaction.returns({ + sdkClientStub.submitEthereumTransaction.resolves({ txResponse: { transactionId: '', - }, + } as unknown as TransactionResponse, fileId: null, }); - const response = (await ethImpl.sendRawTransaction(signed, getRequestId())) as JsonRpcError; + const response = (await ethImpl.sendRawTransaction(signed, requestDetails)) as JsonRpcError; expect(response.code).to.equal(predefined.INTERNAL_ERROR().code); expect(`Error invoking RPC: ${response.message}`).to.equal(predefined.INTERNAL_ERROR(response.message).message); @@ -167,31 +169,31 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () it('should return hash from ContractResult mirror node api', async function () { restMock.onGet(contractResultEndpoint).reply(200, { hash: ethereumHash }); - sdkClientStub.submitEthereumTransaction.returns({ + sdkClientStub.submitEthereumTransaction.resolves({ txResponse: { transactionId: TransactionId.fromString(transactionIdServicesFormat), - }, + } as unknown as TransactionResponse, fileId: null, }); const signed = await signTransaction(transaction); - const resultingHash = await ethImpl.sendRawTransaction(signed, getRequestId()); + const resultingHash = await ethImpl.sendRawTransaction(signed, requestDetails); expect(resultingHash).to.equal(ethereumHash); }); it('should not send second transaction upon succession', async function () { restMock.onGet(contractResultEndpoint).reply(200, { hash: ethereumHash }); - sdkClientStub.submitEthereumTransaction.returns({ + sdkClientStub.submitEthereumTransaction.resolves({ txResponse: { transactionId: TransactionId.fromString(transactionIdServicesFormat), - }, + } as unknown as TransactionResponse, fileId: null, }); const signed = await signTransaction(transaction); - const resultingHash = await ethImpl.sendRawTransaction(signed, getRequestId()); + const resultingHash = await ethImpl.sendRawTransaction(signed, requestDetails); expect(resultingHash).to.equal(ethereumHash); sinon.assert.calledOnce(sdkClientStub.submitEthereumTransaction); }); @@ -203,7 +205,7 @@ describe('@ethSendRawTransaction eth_sendRawTransaction spec', async function () const signed = await signTransaction(transaction); - const response = (await ethImpl.sendRawTransaction(signed, getRequestId())) as JsonRpcError; + const response = (await ethImpl.sendRawTransaction(signed, requestDetails)) as JsonRpcError; expect(response.code).to.equal(predefined.INTERNAL_ERROR().code); expect(`Error invoking RPC: ${response.message}`).to.equal(predefined.INTERNAL_ERROR(response.message).message); sinon.assert.calledOnce(sdkClientStub.submitEthereumTransaction); diff --git a/packages/relay/tests/lib/hapiService.spec.ts b/packages/relay/tests/lib/hapiService.spec.ts index 1aeed76d36..856d26d04e 100644 --- a/packages/relay/tests/lib/hapiService.spec.ts +++ b/packages/relay/tests/lib/hapiService.spec.ts @@ -29,6 +29,7 @@ import { SDKClient } from '../../src/lib/clients'; import HbarLimit from '../../src/lib/hbarlimiter'; import HAPIService from '../../src/lib/services/hapiService/hapiService'; import { CacheService } from '../../src/lib/services/cacheService/cacheService'; +import { RequestDetails } from '../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); @@ -43,6 +44,7 @@ describe('HAPI Service', async function () { let hapiService: HAPIService; const errorStatus = 50; + const requestDetails = new RequestDetails({ requestId: 'hapiService.spec.ts', ipAddress: '0.0.0.0' }); this.beforeAll(() => { const duration: number = 60000; @@ -147,7 +149,7 @@ describe('HAPI Service', async function () { const oldClientInstance = hapiService.getMainClientInstance(); const oldSDKInstance = hapiService.getSDKClient(); - hbarLimiter.addExpense(costAmount, Date.now()); + hbarLimiter.addExpense(costAmount, Date.now(), requestDetails); hapiService.decrementErrorCounter(errorStatus); const newSDKInstance = hapiService.getSDKClient(); diff --git a/packages/relay/tests/lib/hbarLimiter.spec.ts b/packages/relay/tests/lib/hbarLimiter.spec.ts index 4e02e478ae..2d1e45f35c 100644 --- a/packages/relay/tests/lib/hbarLimiter.spec.ts +++ b/packages/relay/tests/lib/hbarLimiter.spec.ts @@ -23,6 +23,7 @@ import { expect } from 'chai'; import { Registry } from 'prom-client'; import HbarLimit from '../../src/lib/hbarlimiter'; import { estimateFileTransactionsFee, getRequestId, random20BytesAddress } from '../helpers'; +import { RequestDetails } from '../../src/lib/types'; const registry = new Registry(); const logger = pino(); @@ -42,6 +43,7 @@ describe('HBAR Rate Limiter', async function () { const randomAccountAddress = random20BytesAddress(); const randomWhiteListedAccountAddress = random20BytesAddress(); const fileChunkSize = Number(process.env.FILE_APPEND_CHUNK_SIZE) || 5120; + const requestDetails = new RequestDetails({ requestId: getRequestId(), ipAddress: '0.0.0.0' }); this.beforeEach(() => { currentDateNow = Date.now(); @@ -67,8 +69,9 @@ describe('HBAR Rate Limiter', async function () { 'QUERY', 'eth_call', randomAccountAddress, + requestDetails, ); - rateLimiterWithEmptyBudget.addExpense(validTotal, currentDateNow); + rateLimiterWithEmptyBudget.addExpense(validTotal, currentDateNow, requestDetails); expect(isEnabled).to.equal(false); expect(shouldRateLimit).to.equal(false); @@ -82,8 +85,14 @@ describe('HBAR Rate Limiter', async function () { const isEnabled = rateLimiter.isEnabled(); const limiterResetTime = rateLimiter.getResetTime(); const limiterRemainingBudget = rateLimiter.getRemainingBudget(); - const shouldRateLimit = rateLimiter.shouldLimit(currentDateNow, 'QUERY', 'eth_call', randomAccountAddress); - rateLimiter.addExpense(validTotal, currentDateNow); + const shouldRateLimit = rateLimiter.shouldLimit( + currentDateNow, + 'QUERY', + 'eth_call', + randomAccountAddress, + requestDetails, + ); + rateLimiter.addExpense(validTotal, currentDateNow, requestDetails); expect(isEnabled).to.equal(false); expect(shouldRateLimit).to.equal(false); @@ -97,8 +106,14 @@ describe('HBAR Rate Limiter', async function () { const isEnabled = invalidRateLimiter.isEnabled(); const limiterResetTime = invalidRateLimiter.getResetTime(); const limiterRemainingBudget = invalidRateLimiter.getRemainingBudget(); - const shouldRateLimit = invalidRateLimiter.shouldLimit(currentDateNow, 'QUERY', 'eth_call', randomAccountAddress); - invalidRateLimiter.addExpense(validTotal, currentDateNow); + const shouldRateLimit = invalidRateLimiter.shouldLimit( + currentDateNow, + 'QUERY', + 'eth_call', + randomAccountAddress, + requestDetails, + ); + invalidRateLimiter.addExpense(validTotal, currentDateNow, requestDetails); expect(isEnabled).to.equal(false); expect(shouldRateLimit).to.equal(false); @@ -110,7 +125,13 @@ describe('HBAR Rate Limiter', async function () { const isEnabled = rateLimiter.isEnabled(); const limiterResetTime = rateLimiter.getResetTime(); const limiterRemainingBudget = rateLimiter.getRemainingBudget(); - const shouldRateLimit = rateLimiter.shouldLimit(currentDateNow, 'QUERY', 'eth_call', randomAccountAddress); + const shouldRateLimit = rateLimiter.shouldLimit( + currentDateNow, + 'QUERY', + 'eth_call', + randomAccountAddress, + requestDetails, + ); expect(isEnabled).to.equal(true); expect(shouldRateLimit).to.equal(false); @@ -121,7 +142,7 @@ describe('HBAR Rate Limiter', async function () { it('should not rate limit', async function () { const cost = 10000000; - rateLimiter.addExpense(cost, currentDateNow); + rateLimiter.addExpense(cost, currentDateNow, requestDetails); const isEnabled = rateLimiter.isEnabled(); const limiterResetTime = rateLimiter.getResetTime(); @@ -131,6 +152,7 @@ describe('HBAR Rate Limiter', async function () { 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); expect(isEnabled).to.equal(true); @@ -142,7 +164,7 @@ describe('HBAR Rate Limiter', async function () { it('should rate limit', async function () { const cost = 1000000000; - rateLimiter.addExpense(cost, currentDateNow); + rateLimiter.addExpense(cost, currentDateNow, requestDetails); const isEnabled = rateLimiter.isEnabled(); const limiterResetTime = rateLimiter.getResetTime(); @@ -152,6 +174,7 @@ describe('HBAR Rate Limiter', async function () { 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); expect(isEnabled).to.equal(true); @@ -163,7 +186,7 @@ describe('HBAR Rate Limiter', async function () { it('should reset budget, while checking if we should rate limit', async function () { const cost = 1000000000; - rateLimiter.addExpense(cost, currentDateNow); + rateLimiter.addExpense(cost, currentDateNow, requestDetails); const isEnabled = rateLimiter.isEnabled(); const futureDate = currentDateNow + validDuration * 2; @@ -172,6 +195,7 @@ describe('HBAR Rate Limiter', async function () { 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); const limiterResetTime = rateLimiter.getResetTime(); const limiterRemainingBudget = rateLimiter.getRemainingBudget(); @@ -185,21 +209,23 @@ describe('HBAR Rate Limiter', async function () { it('should reset budget, while adding expense', async function () { const cost = 1000000000; - rateLimiter.addExpense(cost, currentDateNow); + rateLimiter.addExpense(cost, currentDateNow, requestDetails); const shouldRateLimitBefore = rateLimiter.shouldLimit( currentDateNow, 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); const futureDate = currentDateNow + validDuration * 2; - rateLimiter.addExpense(100, futureDate); + rateLimiter.addExpense(100, futureDate, requestDetails); const shouldRateLimitAfter = rateLimiter.shouldLimit( futureDate, 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); const isEnabled = rateLimiter.isEnabled(); @@ -219,7 +245,7 @@ describe('HBAR Rate Limiter', async function () { callDataSize, fileChunkSize, mockedExchangeRateInCents, - getRequestId(), + requestDetails, ); expect(result).to.be.true; }); @@ -230,7 +256,7 @@ describe('HBAR Rate Limiter', async function () { callDataSize, fileChunkSize, mockedExchangeRateInCents, - getRequestId(), + requestDetails, ); expect(result).to.be.false; }); @@ -245,7 +271,7 @@ describe('HBAR Rate Limiter', async function () { it('should bypass rate limit if original caller is a white listed account', async function () { // add expense to rate limit throttle - rateLimiter.addExpense(validTotal, currentDateNow); + rateLimiter.addExpense(validTotal, currentDateNow, requestDetails); // should return true as `randomAccountAddress` is not white listed const shouldNOTByPassRateLimit = rateLimiter.shouldLimit( @@ -253,6 +279,7 @@ describe('HBAR Rate Limiter', async function () { 'TRANSACTION', 'eth_sendRawTransaction', randomAccountAddress, + requestDetails, ); // should return false as `randomWhiteListedAccountAddress` is white listed @@ -261,6 +288,7 @@ describe('HBAR Rate Limiter', async function () { 'TRANSACTION', 'eth_sendRawTransaction', randomWhiteListedAccountAddress, + requestDetails, ); expect(shouldByPassRateLimit).to.equal(false); @@ -273,7 +301,7 @@ describe('HBAR Rate Limiter', async function () { callDataSize, fileChunkSize, mockedExchangeRateInCents, - getRequestId(), + requestDetails, ); expect(result).to.be.false; }); @@ -284,7 +312,7 @@ describe('HBAR Rate Limiter', async function () { callDataSize, fileChunkSize, mockedExchangeRateInCents, - getRequestId(), + requestDetails, ); expect(result).to.be.true; }); diff --git a/packages/relay/tests/lib/mirrorNodeClient.spec.ts b/packages/relay/tests/lib/mirrorNodeClient.spec.ts index 88172e44b8..f016084f4e 100644 --- a/packages/relay/tests/lib/mirrorNodeClient.spec.ts +++ b/packages/relay/tests/lib/mirrorNodeClient.spec.ts @@ -23,26 +23,30 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import { Registry } from 'prom-client'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); -import { MirrorNodeClient } from '../../src/lib/clients/mirrorNodeClient'; +import { MirrorNodeClient } from '../../src/lib/clients'; import constants from '../../src/lib/constants'; -import axios from 'axios'; +import axios, { AxiosInstance } from 'axios'; import MockAdapter from 'axios-mock-adapter'; -import { getRequestId, mockData, random20BytesAddress } from './../helpers'; +import { getRequestId, mockData, random20BytesAddress } from '../helpers'; + const registry = new Registry(); import pino from 'pino'; import { ethers } from 'ethers'; -import { predefined } from '../../src/lib/errors/JsonRpcError'; -import { SDKClientError } from '../../src/lib/errors/SDKClientError'; +import { predefined, MirrorNodeClientError } from '../../src'; import { CacheService } from '../../src/lib/services/cacheService/cacheService'; -import { MirrorNodeClientError } from '../../src/lib/errors/MirrorNodeClientError'; +import { MirrorNodeTransactionRecord, RequestDetails } from '../../src/lib/types'; +import { SDKClientError } from '../../src/lib/errors/SDKClientError'; +import { BigNumber } from 'bignumber.js'; + const logger = pino(); const noTransactions = '?transactions=false'; +const requestDetails = new RequestDetails({ requestId: getRequestId(), ipAddress: '0.0.0.0' }); describe('MirrorNodeClient', async function () { this.timeout(20000); - let instance, mock, mirrorNodeInstance, cacheService; + let instance: AxiosInstance, mock: MockAdapter, mirrorNodeInstance: MirrorNodeClient, cacheService: CacheService; before(() => { // mock axios @@ -66,7 +70,7 @@ describe('MirrorNodeClient', async function () { beforeEach(() => { mock = new MockAdapter(instance); - mirrorNodeInstance.cacheService.clear(); + cacheService.clear(requestDetails); }); describe('handleError', async () => { @@ -79,12 +83,13 @@ describe('MirrorNodeClient', async function () { let error = new Error('test error'); error['response'] = 'test error'; - const result = await mirrorNodeInstance.handleError( + const result = mirrorNodeInstance.handleError( error, CONTRACT_CALL_ENDPOINT, CONTRACT_CALL_ENDPOINT, code, 'POST', + requestDetails, ); expect(result).to.equal(null); }); @@ -95,9 +100,16 @@ describe('MirrorNodeClient', async function () { try { let error = new Error('test error'); error['response'] = 'test error'; - await mirrorNodeInstance.handleError(error, CONTRACT_CALL_ENDPOINT, CONTRACT_CALL_ENDPOINT, code, 'POST'); + mirrorNodeInstance.handleError( + error, + CONTRACT_CALL_ENDPOINT, + CONTRACT_CALL_ENDPOINT, + code, + 'POST', + requestDetails, + ); expect.fail('should have thrown an error'); - } catch (e) { + } catch (e: any) { expect(e.message).to.equal('test error'); } }); @@ -107,20 +119,20 @@ describe('MirrorNodeClient', async function () { it('Can extract the account number out of an account pagination next link url', async () => { const accountId = '0.0.123'; const url = `/api/v1/accounts/${accountId}?limit=100×tamp=lt:1682455406.562695326`; - const extractedAccountId = mirrorNodeInstance.extractAccountIdFromUrl(url); + const extractedAccountId = mirrorNodeInstance.extractAccountIdFromUrl(url, requestDetails); expect(extractedAccountId).to.eq(accountId); }); it('Can extract the evm address out of an account pagination next link url', async () => { const evmAddress = '0x583031d1113ad414f02576bd6afa5bbdf935b7d9'; const url = `/api/v1/accounts/${evmAddress}?limit=100×tamp=lt:1682455406.562695326`; - const extractedEvmAddress = mirrorNodeInstance.extractAccountIdFromUrl(url); + const extractedEvmAddress = mirrorNodeInstance.extractAccountIdFromUrl(url, requestDetails); expect(extractedEvmAddress).to.eq(evmAddress); }); it('it should have a `request` method ', async () => { expect(mirrorNodeInstance).to.exist; - expect(mirrorNodeInstance.request).to.exist; + expect(mirrorNodeInstance['request']).to.exist; }); it('`restUrl` is exposed and correct', async () => { @@ -137,14 +149,14 @@ describe('MirrorNodeClient', async function () { it('Can extract the account number out of an account pagination next link url', async () => { const accountId = '0.0.123'; const url = `/api/v1/accounts/${accountId}?limit=100×tamp=lt:1682455406.562695326`; - const extractedAccountId = mirrorNodeInstance.extractAccountIdFromUrl(url); + const extractedAccountId = mirrorNodeInstance.extractAccountIdFromUrl(url, requestDetails); expect(extractedAccountId).to.eq(accountId); }); it('Can extract the evm address out of an account pagination next link url', async () => { const evmAddress = '0x583031d1113ad414f02576bd6afa5bbdf935b7d9'; const url = `/api/v1/accounts/${evmAddress}?limit=100×tamp=lt:1682455406.562695326`; - const extractedEvmAddress = mirrorNodeInstance.extractAccountIdFromUrl(url); + const extractedEvmAddress = mirrorNodeInstance.extractAccountIdFromUrl(url, requestDetails); expect(extractedEvmAddress).to.eq(evmAddress); }); @@ -227,7 +239,7 @@ describe('MirrorNodeClient', async function () { }, }); - const result = await mirrorNodeInstance.get('accounts'); + const result = await mirrorNodeInstance.get('accounts', 'accounts', requestDetails); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.exist; @@ -247,7 +259,7 @@ describe('MirrorNodeClient', async function () { }; mock.onPost('contracts/call', { foo: 'bar' }).reply(200, mockResult); - const result = await mirrorNodeInstance.post('contracts/call', { foo: 'bar' }); + const result = await mirrorNodeInstance.post('contracts/call', { foo: 'bar' }, 'contracts/call', requestDetails); expect(result).to.exist; expect(result.result).to.exist; expect(result.result).to.eq(mockResult.result); @@ -255,7 +267,7 @@ describe('MirrorNodeClient', async function () { it('call to non-existing REST route returns 404', async () => { try { - expect(await mirrorNodeInstance.get('non-existing-route')).to.throw(); + expect(await mirrorNodeInstance.get('non-existing-route', 'non-existing-route', requestDetails)).to.throw; } catch (err: any) { expect(err.statusCode).to.eq(404); } @@ -274,7 +286,7 @@ describe('MirrorNodeClient', async function () { }, }); - const result = await mirrorNodeInstance.getAccount(alias); + const result = await mirrorNodeInstance.getAccount(alias, requestDetails); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -299,7 +311,7 @@ describe('MirrorNodeClient', async function () { }, }); - const result = await mirrorNodeInstance.getBlock(hash); + const result = await mirrorNodeInstance.getBlock(hash, requestDetails); expect(result).to.exist; expect(result.count).equal(3); expect(result.number).equal(77); @@ -322,7 +334,7 @@ describe('MirrorNodeClient', async function () { }, }); - const result = await mirrorNodeInstance.getBlock(number); + const result = await mirrorNodeInstance.getBlock(number, requestDetails); expect(result).to.exist; expect(result.count).equal(3); expect(result.number).equal(77); @@ -347,7 +359,7 @@ describe('MirrorNodeClient', async function () { .onGet(`blocks?block.number=${number}&limit=100&order=asc`) .reply(200, { blocks: [block], links: { next: null } }); - const result = await mirrorNodeInstance.getBlocks(number); + const result = await mirrorNodeInstance.getBlocks(requestDetails, number); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -363,7 +375,7 @@ describe('MirrorNodeClient', async function () { .onGet(`blocks?timestamp=${timestamp}&limit=100&order=asc`) .reply(200, { blocks: [block], links: { next: null } }); - const result = await mirrorNodeInstance.getBlocks(undefined, timestamp); + const result = await mirrorNodeInstance.getBlocks(requestDetails, undefined, timestamp); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -375,21 +387,21 @@ describe('MirrorNodeClient', async function () { it('`getContract`', async () => { mock.onGet(`contracts/${mockData.contractEvmAddress}`).reply(200, mockData.contract); - const result = await mirrorNodeInstance.getContract(mockData.contractEvmAddress); + const result = await mirrorNodeInstance.getContract(mockData.contractEvmAddress, requestDetails); expect(result).to.exist; expect(result.contract_id).equal('0.0.2000'); }); it('`getContract` not found', async () => { mock.onGet(`contracts/${mockData.contractEvmAddress}`).reply(404, mockData.notFound); - const result = await mirrorNodeInstance.getContract(mockData.contractEvmAddress); + const result = await mirrorNodeInstance.getContract(mockData.contractEvmAddress, requestDetails); expect(result).to.be.null; }); it('`getAccount`', async () => { mock.onGet(`accounts/${mockData.accountEvmAddress}${noTransactions}`).reply(200, mockData.account); - const result = await mirrorNodeInstance.getAccount(mockData.accountEvmAddress); + const result = await mirrorNodeInstance.getAccount(mockData.accountEvmAddress, requestDetails); expect(result).to.exist; expect(result.account).equal('0.0.1014'); }); @@ -398,7 +410,7 @@ describe('MirrorNodeClient', async function () { const evmAddress = '0x00000000000000000000000000000000000003f6'; mock.onGet(`accounts/${evmAddress}${noTransactions}`).reply(404, mockData.notFound); - const result = await mirrorNodeInstance.getAccount(evmAddress); + const result = await mirrorNodeInstance.getAccount(evmAddress, requestDetails); expect(result).to.be.null; }); @@ -407,7 +419,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${evmAddress}${noTransactions}`).reply(500, { error: 'unexpected error' }); let errorRaised = false; try { - await mirrorNodeInstance.getAccount(evmAddress); + await mirrorNodeInstance.getAccount(evmAddress, requestDetails); } catch (error: any) { errorRaised = true; expect(error.message).to.equal(`Request failed with status code 500`); @@ -420,7 +432,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${invalidAddress}${noTransactions}`).reply(400); let errorRaised = false; try { - await mirrorNodeInstance.getAccount(invalidAddress); + await mirrorNodeInstance.getAccount(invalidAddress, requestDetails); } catch (error: any) { errorRaised = true; expect(error.message).to.equal(`Request failed with status code 400`); @@ -431,7 +443,7 @@ describe('MirrorNodeClient', async function () { it('`getTokenById`', async () => { mock.onGet(`tokens/${mockData.tokenId}`).reply(200, mockData.token); - const result = await mirrorNodeInstance.getTokenById(mockData.tokenId); + const result = await mirrorNodeInstance.getTokenById(mockData.tokenId, requestDetails); expect(result).to.exist; expect(result.token_id).equal('0.0.13312'); }); @@ -440,7 +452,7 @@ describe('MirrorNodeClient', async function () { const tokenId = '0.0.132'; mock.onGet(`accounts/${tokenId}${noTransactions}`).reply(404, mockData.notFound); - const result = await mirrorNodeInstance.getTokenById(tokenId); + const result = await mirrorNodeInstance.getTokenById(tokenId, requestDetails); expect(result).to.be.null; }); @@ -519,7 +531,7 @@ describe('MirrorNodeClient', async function () { const transactionId = '0.0.10-167654-000123456'; mock.onGet(`contracts/results/${transactionId}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResult(transactionId); + const result = await mirrorNodeInstance.getContractResult(transactionId, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -530,7 +542,7 @@ describe('MirrorNodeClient', async function () { const hash = '0x4a563af33c4871b51a8b108aa2fe1dd5280a30dfb7236170ae5e5e7957eb6391'; mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResult(hash); + const result = await mirrorNodeInstance.getContractResult(hash, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -540,10 +552,10 @@ describe('MirrorNodeClient', async function () { it('`getContractResults` by hash using cache', async () => { const hash = '0x07cad7b827375d10d73af57b6a3e84353645fdb1305ea58ff52dda53ec640533'; mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const resultBeforeCached = await mirrorNodeInstance.getContractResult(hash); + const resultBeforeCached = await mirrorNodeInstance.getContractResult(hash, requestDetails); mock.onGet(`contracts/results/${hash}`).reply(400, null); - const resultAfterCached = await mirrorNodeInstance.getContractResult(hash); + const resultAfterCached = await mirrorNodeInstance.getContractResult(hash, requestDetails); expect(resultBeforeCached).to.eq(resultAfterCached); }); @@ -552,7 +564,7 @@ describe('MirrorNodeClient', async function () { const hash = '0x4a563af33c4871b51a8b108aa2fe1dd5280a30dfb7236170ae5e5e7957eb6399'; mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResultWithRetry(hash); + const result = await mirrorNodeInstance.getContractResultWithRetry(hash, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -566,7 +578,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(`contracts/results/${hash}`).replyOnce(200, { ...detailedContractResult, transaction_index: undefined }); mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResultWithRetry(hash); + const result = await mirrorNodeInstance.getContractResultWithRetry(hash, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -582,7 +594,7 @@ describe('MirrorNodeClient', async function () { .replyOnce(200, { ...detailedContractResult, transaction_index: undefined, block_number: undefined }); mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResultWithRetry(hash); + const result = await mirrorNodeInstance.getContractResultWithRetry(hash, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -597,7 +609,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(`contracts/results/${hash}`).replyOnce(200, { ...detailedContractResult, block_number: undefined }); mock.onGet(`contracts/results/${hash}`).reply(200, detailedContractResult); - const result = await mirrorNodeInstance.getContractResultWithRetry(hash); + const result = await mirrorNodeInstance.getContractResultWithRetry(hash, requestDetails); expect(result).to.exist; expect(result.contract_id).equal(detailedContractResult.contract_id); expect(result.to).equal(detailedContractResult.to); @@ -611,7 +623,7 @@ describe('MirrorNodeClient', async function () { .onGet(`contracts/results?limit=100&order=asc`) .reply(200, { results: [detailedContractResult], links: { next: null } }); - const result = await mirrorNodeInstance.getContractResults(); + const result = await mirrorNodeInstance.getContractResults(requestDetails); expect(result).to.exist; expect(result.links).to.not.exist; expect(result.length).to.gt(0); @@ -630,8 +642,8 @@ describe('MirrorNodeClient', async function () { error_message: null, from: '0x0000000000000000000000000000000000001f41', function_parameters: '0x0707', - gas_limit: 9223372036854775807, - gas_used: 9223372036854775806, + gas_limit: BigNumber('9223372036854775807'), + gas_used: BigNumber('9223372036854775806'), timestamp: '987654.000123456', to: '0x0000000000000000000000000000000000001389', }; @@ -641,7 +653,7 @@ describe('MirrorNodeClient', async function () { .onGet(`contracts/${contractId}/results?limit=100&order=asc`) .reply(200, { results: [contractResult], links: { next: null } }); - const result = await mirrorNodeInstance.getContractResultsByAddress(contractId); + const result = await mirrorNodeInstance.getContractResultsByAddress(contractId, requestDetails); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -658,7 +670,7 @@ describe('MirrorNodeClient', async function () { .onGet(`contracts/${address}/results?limit=100&order=asc`) .reply(200, { results: [contractResult], links: { next: null } }); - const result = await mirrorNodeInstance.getContractResultsByAddress(address); + const result = await mirrorNodeInstance.getContractResultsByAddress(address, requestDetails); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -675,7 +687,7 @@ describe('MirrorNodeClient', async function () { .onGet(`contracts/${address}/results?limit=1&order=desc`) .reply(200, { results: [contractResult], links: { next: null } }); - const result = await mirrorNodeInstance.getLatestContractResultsByAddress(address, undefined, 1); + const result = await mirrorNodeInstance.getLatestContractResultsByAddress(address, undefined, 1, requestDetails); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -692,7 +704,12 @@ describe('MirrorNodeClient', async function () { .onGet(`contracts/${address}/results?timestamp=lte:987654.000123456&limit=2&order=desc`) .reply(200, { results: [contractResult], links: { next: null } }); - const result = await mirrorNodeInstance.getLatestContractResultsByAddress(address, '987654.000123456', 2); + const result = await mirrorNodeInstance.getLatestContractResultsByAddress( + address, + '987654.000123456', + 2, + requestDetails, + ); expect(result).to.exist; expect(result.links).to.exist; expect(result.links.next).to.equal(null); @@ -716,7 +733,7 @@ describe('MirrorNodeClient', async function () { it('`getContractResultsLogs` ', async () => { mock.onGet(`contracts/results/logs?limit=100&order=asc`).reply(200, { logs: [log] }); - const results = await mirrorNodeInstance.getContractResultsLogs(); + const results = await mirrorNodeInstance.getContractResultsLogs(requestDetails); expect(results).to.exist; expect(results.length).to.gt(0); const firstResult = results[0]; @@ -728,7 +745,7 @@ describe('MirrorNodeClient', async function () { it('`getContractResultsLogsByAddress` ', async () => { mock.onGet(`contracts/${log.address}/results/logs?limit=100&order=asc`).reply(200, { logs: [log] }); - const results = await mirrorNodeInstance.getContractResultsLogsByAddress(log.address); + const results = await mirrorNodeInstance.getContractResultsLogsByAddress(log.address, requestDetails); expect(results).to.exist; expect(results.length).to.gt(0); const firstResult = results[0]; @@ -737,7 +754,7 @@ describe('MirrorNodeClient', async function () { expect(firstResult.index).equal(log.index); }); it('`getContractResultsLogsByAddress` with ZeroAddress ', async () => { - const results = await mirrorNodeInstance.getContractResultsLogsByAddress(ethers.ZeroAddress); + const results = await mirrorNodeInstance.getContractResultsLogsByAddress(ethers.ZeroAddress, requestDetails); expect(results).to.exist; expect(results.length).to.eq(0); expect(results).to.deep.equal([]); @@ -752,6 +769,7 @@ describe('MirrorNodeClient', async function () { const result = await mirrorNodeInstance.getContractStateByAddressAndSlot( contractAddress, defaultCurrentContractState.state[0].slot, + requestDetails, ); expect(result).to.exist; @@ -770,6 +788,7 @@ describe('MirrorNodeClient', async function () { await mirrorNodeInstance.getContractStateByAddressAndSlot( contractAddress + '1', defaultCurrentContractState.state[0].slot, + requestDetails, ), ).to.throw(); } catch (error) { @@ -788,6 +807,7 @@ describe('MirrorNodeClient', async function () { await mirrorNodeInstance.getContractStateByAddressAndSlot( contractAddress, defaultCurrentContractState.state[0].slot + '1', + requestDetails, ), ).to.throw(); } catch (error) { @@ -800,7 +820,7 @@ describe('MirrorNodeClient', async function () { const incorrectAddress = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ed'; try { - expect(await mirrorNodeInstance.getContractResultsLogsByAddress(incorrectAddress)).to.throw(); + expect(await mirrorNodeInstance.getContractResultsLogsByAddress(incorrectAddress, requestDetails)).to.throw; } catch (err: any) { expect(err).to.exist; } @@ -809,7 +829,7 @@ describe('MirrorNodeClient', async function () { it('`getBlocks` by number', async () => { mock.onGet(`blocks?limit=1&order=desc`).reply(200, block); - const result = await mirrorNodeInstance.getLatestBlock(); + const result = await mirrorNodeInstance.getLatestBlock(requestDetails); expect(result).to.exist; expect(result.count).equal(block.count); expect(result.number).equal(block.number); @@ -823,7 +843,7 @@ describe('MirrorNodeClient', async function () { }); for (let i = 0; i < 3; i++) { - const result = await mirrorNodeInstance.getBlock(hash); + const result = await mirrorNodeInstance.getBlock(hash, requestDetails); expect(result).to.exist; expect(result.hash).equal(hash); expect(result.number).equal(77); @@ -847,7 +867,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(`network/exchangerate`).reply(200, exchangerate); - const result = await mirrorNodeInstance.getNetworkExchangeRate(getRequestId()); + const result = await mirrorNodeInstance.getNetworkExchangeRate(requestDetails); expect(result).to.exist; expect(result.current_rate).to.exist; expect(result.next_rate).to.exist; @@ -864,13 +884,17 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${mockData.contractEvmAddress}${noTransactions}`).reply(200, mockData.account); mock.onGet(`tokens/${mockData.contractEvmAddress}`).reply(404, mockData.notFound); - const entityType = await mirrorNodeInstance.resolveEntityType(mockData.contractEvmAddress); + const entityType = await mirrorNodeInstance.resolveEntityType( + mockData.contractEvmAddress, + 'mirrorNodeClientTest', + requestDetails, + ); expect(entityType).to.exist; expect(entityType).to.have.property('type'); expect(entityType).to.have.property('entity'); - expect(entityType.type).to.eq('contract'); - expect(entityType.entity).to.have.property('contract_id'); - expect(entityType.entity.contract_id).to.eq(mockData.contract.contract_id); + expect(entityType!.type).to.eq('contract'); + expect(entityType!.entity).to.have.property('contract_id'); + expect(entityType!.entity.contract_id).to.eq(mockData.contract.contract_id); }); it('returns `account` when CONTRACTS and TOKENS endpoint returns 404 and ACCOUNTS endpoint returns a result', async () => { @@ -878,13 +902,17 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${mockData.accountEvmAddress}${noTransactions}`).reply(200, mockData.account); mock.onGet(`tokens/${mockData.tokenId}`).reply(404, mockData.notFound); - const entityType = await mirrorNodeInstance.resolveEntityType(mockData.accountEvmAddress); + const entityType = await mirrorNodeInstance.resolveEntityType( + mockData.accountEvmAddress, + 'mirrorNodeClientTest', + requestDetails, + ); expect(entityType).to.exist; expect(entityType).to.have.property('type'); expect(entityType).to.have.property('entity'); - expect(entityType.type).to.eq('account'); - expect(entityType.entity).to.have.property('account'); - expect(entityType.entity.account).to.eq(mockData.account.account); + expect(entityType!.type).to.eq('account'); + expect(entityType!.entity).to.have.property('account'); + expect(entityType!.entity.account).to.eq(mockData.account.account); }); it('returns `token` when CONTRACTS and ACCOUNTS endpoints returns 404 and TOKEN endpoint returns a result', async () => { @@ -892,12 +920,16 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${notFoundAddress}${noTransactions}`).reply(404, mockData.notFound); mock.onGet(`tokens/${mockData.tokenId}`).reply(200, mockData.token); - const entityType = await mirrorNodeInstance.resolveEntityType(mockData.tokenLongZero); + const entityType = await mirrorNodeInstance.resolveEntityType( + mockData.tokenLongZero, + 'mirrorNodeClientTest', + requestDetails, + ); expect(entityType).to.exist; expect(entityType).to.have.property('type'); expect(entityType).to.have.property('entity'); - expect(entityType.type).to.eq('token'); - expect(entityType.entity.token_id).to.eq(mockData.tokenId); + expect(entityType!.type).to.eq('token'); + expect(entityType!.entity.token_id).to.eq(mockData.tokenId); }); it('returns null when CONTRACTS and ACCOUNTS endpoints return 404', async () => { @@ -905,7 +937,11 @@ describe('MirrorNodeClient', async function () { mock.onGet(`accounts/${notFoundAddress}${noTransactions}`).reply(404, mockData.notFound); mock.onGet(`tokens/${notFoundAddress}`).reply(404, mockData.notFound); - const entityType = await mirrorNodeInstance.resolveEntityType(notFoundAddress); + const entityType = await mirrorNodeInstance.resolveEntityType( + notFoundAddress, + 'mirrorNodeClientTest', + requestDetails, + ); expect(entityType).to.be.null; }); @@ -913,31 +949,35 @@ describe('MirrorNodeClient', async function () { mock.onGet(`contracts/${mockData.tokenId}`).reply(404, mockData.notFound); mock.onGet(`tokens/${mockData.tokenId}`).reply(200, mockData.token); - const entityType = await mirrorNodeInstance.resolveEntityType(mockData.tokenLongZero, [ - constants.TYPE_CONTRACT, - constants.TYPE_TOKEN, - ]); + const entityType = await mirrorNodeInstance.resolveEntityType( + mockData.tokenLongZero, + 'mirrorNodeClientTest', + requestDetails, + [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], + ); expect(entityType).to.exist; expect(entityType).to.have.property('type'); expect(entityType).to.have.property('entity'); - expect(entityType.type).to.eq('token'); - expect(entityType.entity.token_id).to.eq(mockData.tokenId); + expect(entityType!.type).to.eq('token'); + expect(entityType!.entity.token_id).to.eq(mockData.tokenId); }); it('does not call mirror node tokens API when token is not long zero type', async () => { mock.onGet(`contracts/${mockData.contractEvmAddress}`).reply(200, mockData.contract); mock.onGet(`tokens/${mockData.tokenId}`).reply(404, mockData.notFound); - const entityType = await mirrorNodeInstance.resolveEntityType(mockData.contractEvmAddress, [ - constants.TYPE_CONTRACT, - constants.TYPE_TOKEN, - ]); + const entityType = await mirrorNodeInstance.resolveEntityType( + mockData.contractEvmAddress, + 'mirrorNodeClientTest', + requestDetails, + [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], + ); expect(entityType).to.exist; expect(entityType).to.have.property('type'); expect(entityType).to.have.property('entity'); - expect(entityType.type).to.eq('contract'); - expect(entityType.entity).to.have.property('contract_id'); - expect(entityType.entity.contract_id).to.eq(mockData.contract.contract_id); + expect(entityType!.type).to.eq('contract'); + expect(entityType!.entity).to.have.property('contract_id'); + expect(entityType!.entity.contract_id).to.eq(mockData.contract.contract_id); }); }); @@ -994,7 +1034,7 @@ describe('MirrorNodeClient', async function () { it('should be able to fetch transaction by transaction id', async () => { mock.onGet(`transactions/${defaultTransactionIdFormatted}`).reply(200, defaultTransaction); - const transaction = await mirrorNodeInstance.getTransactionById(defaultTransactionId); + const transaction = await mirrorNodeInstance.getTransactionById(defaultTransactionId, requestDetails); expect(transaction).to.exist; expect(transaction.transactions.length).to.equal(defaultTransaction.transactions.length); }); @@ -1003,7 +1043,7 @@ describe('MirrorNodeClient', async function () { mock .onGet(`transactions/${defaultTransactionIdFormatted}?nonce=1`) .reply(200, defaultTransaction.transactions[1]); - const transaction = await mirrorNodeInstance.getTransactionById(defaultTransactionId, 1); + const transaction = await mirrorNodeInstance.getTransactionById(defaultTransactionId, requestDetails, 1); expect(transaction).to.exist; expect(transaction.transaction_id).to.equal(defaultTransaction.transactions[1].transaction_id); expect(transaction.result).to.equal(defaultTransaction.transactions[1].result); @@ -1011,7 +1051,7 @@ describe('MirrorNodeClient', async function () { it('should fail to fetch transaction by wrong transaction id', async () => { mock.onGet(`transactions/${invalidTransactionId}`).reply(404, mockData.notFound); - const transaction = await mirrorNodeInstance.getTransactionById(invalidTransactionId); + const transaction = await mirrorNodeInstance.getTransactionById(invalidTransactionId, requestDetails); expect(transaction).to.be.null; }); @@ -1022,7 +1062,7 @@ describe('MirrorNodeClient', async function () { }); mock.onGet(`transactions/${transactionId}`).reply(200, null); - const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, getRequestId()); + const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, requestDetails); expect(result).to.be.null; }); @@ -1033,7 +1073,7 @@ describe('MirrorNodeClient', async function () { }); mock.onGet(`transactions/${transactionId}`).reply(200, []); - const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, getRequestId()); + const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, requestDetails); expect(result).to.be.null; }); @@ -1044,7 +1084,7 @@ describe('MirrorNodeClient', async function () { }); mock.onGet(`transactions/${transactionId}`).reply(200, defaultTransaction); - const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, getRequestId()); + const result = await mirrorNodeInstance.getContractRevertReasonFromTransaction(error, requestDetails); expect(result).to.eq('INVALID_FULL_PREFIX_SIGNATURE_FOR_PRECOMPILE'); }); }); @@ -1081,7 +1121,12 @@ describe('MirrorNodeClient', async function () { }, }); - const results = await mirrorNodeInstance.getPaginatedResults('results', 'results', 'genericResults'); + const results = await mirrorNodeInstance.getPaginatedResults( + 'results', + 'results', + 'genericResults', + requestDetails, + ); expect(results).to.exist; expect(results).to.deep.equal(mockedResults); @@ -1091,7 +1136,12 @@ describe('MirrorNodeClient', async function () { const pages = 5; const mockedResults = mockPages(pages); - const results = await mirrorNodeInstance.getPaginatedResults('results?page=0', 'results', 'genericResults'); + const results = await mirrorNodeInstance.getPaginatedResults( + 'results?page=0', + 'results', + 'genericResults', + requestDetails, + ); expect(results).to.exist; expect(results.length).to.eq(pages); @@ -1103,9 +1153,9 @@ describe('MirrorNodeClient', async function () { mockPages(pages); try { - await mirrorNodeInstance.getPaginatedResults('results?page=0', 'results', 'genericResults'); + await mirrorNodeInstance.getPaginatedResults('results?page=0', 'results', 'genericResults', requestDetails); expect.fail('should have thrown an error'); - } catch (e) { + } catch (e: any) { const errorRef = predefined.PAGINATION_MAX(0); // reference error for all properties except message expect(e.message).to.equal( `Exceeded maximum mirror node pagination count: ${constants.MAX_MIRROR_NODE_PAGINATION}`, @@ -1121,7 +1171,11 @@ describe('MirrorNodeClient', async function () { it('if the method returns an immediate result it is called only once', async () => { mock.onGet(uri).reply(200, mockData.account); - const result = await mirrorNodeInstance.repeatedRequest('getAccount', [mockData.accountEvmAddress], 3); + const result = await mirrorNodeInstance.repeatedRequest( + 'getAccount', + [mockData.accountEvmAddress, requestDetails], + 3, + ); expect(result).to.exist; expect(result.account).equal('0.0.1014'); @@ -1132,7 +1186,11 @@ describe('MirrorNodeClient', async function () { // Return data on the second call mock.onGet(uri).replyOnce(404, mockData.notFound).onGet(uri).reply(200, mockData.account); - const result = await mirrorNodeInstance.repeatedRequest('getAccount', [mockData.accountEvmAddress], 3); + const result = await mirrorNodeInstance.repeatedRequest( + 'getAccount', + [mockData.accountEvmAddress, requestDetails], + 3, + ); expect(result).to.exist; expect(result.account).equal('0.0.1014'); @@ -1140,7 +1198,11 @@ describe('MirrorNodeClient', async function () { }); it('method is repeated the specified number of times if no result is found', async () => { - const result = await mirrorNodeInstance.repeatedRequest('getAccount', [mockData.accountEvmAddress], 3); + const result = await mirrorNodeInstance.repeatedRequest( + 'getAccount', + [mockData.accountEvmAddress, requestDetails], + 3, + ); expect(result).to.be.null; expect(mock.history.get.length).to.eq(3); // is called three times }); @@ -1157,7 +1219,11 @@ describe('MirrorNodeClient', async function () { .onGet(uri) .reply(200, mockData.account); - const result = await mirrorNodeInstance.repeatedRequest('getAccount', [mockData.accountEvmAddress], 3); + const result = await mirrorNodeInstance.repeatedRequest( + 'getAccount', + [mockData.accountEvmAddress, requestDetails], + 3, + ); expect(result).to.be.null; expect(mock.history.get.length).to.eq(3); // is called three times }); @@ -1196,9 +1262,9 @@ describe('MirrorNodeClient', async function () { const transactionRecordMetrics = await mirrorNodeInstance.getTransactionRecordMetrics( mockedTransactionId, mockedCallerName, - getRequestId(), mockedConstructorName, operatorAcocuntId, + requestDetails, ); expect(transactionRecordMetrics.transactionFee).to.eq(mockedTxFee); @@ -1211,9 +1277,9 @@ describe('MirrorNodeClient', async function () { await mirrorNodeInstance.getTransactionRecordMetrics( mockedTransactionId, mockedCallerName, - getRequestId(), mockedConstructorName, operatorAcocuntId, + requestDetails, ); expect.fail('should have thrown an error'); @@ -1266,7 +1332,7 @@ describe('MirrorNodeClient', async function () { }; const transactionFee = mirrorNodeInstance.getTransferAmountSumForAccount( - mockedMirrorNodeTransactionRecord.transactions[0], + mockedMirrorNodeTransactionRecord.transactions[0] as MirrorNodeTransactionRecord, accountIdA, ); expect(transactionFee).to.eq(expectedTxFeeForAccountIdA); @@ -1328,6 +1394,7 @@ describe('MirrorNodeClient', async function () { const transactions = await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp( evmAddress, timestamp, + requestDetails, ); expect(transactions).to.be.null; }); @@ -1337,6 +1404,7 @@ describe('MirrorNodeClient', async function () { const transactions = await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp( evmAddress, timestamp, + requestDetails, ); expect(transactions).to.exist; expect(transactions.transactions.length).to.equal(0); @@ -1347,6 +1415,7 @@ describe('MirrorNodeClient', async function () { const transactions = await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp( evmAddress, timestamp, + requestDetails, ); expect(transactions).to.exist; expect(transactions.transactions.length).to.equal(1); @@ -1357,6 +1426,7 @@ describe('MirrorNodeClient', async function () { const transactions = await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp( evmAddress, timestamp, + requestDetails, 2, ); expect(transactions).to.exist; @@ -1368,7 +1438,7 @@ describe('MirrorNodeClient', async function () { mock.onGet(transactionPath(address, 1)).reply(500, { error: 'unexpected error' }); let errorRaised = false; try { - await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp(address, timestamp); + await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp(address, timestamp, requestDetails); } catch (error: any) { errorRaised = true; expect(error.message).to.equal(`Request failed with status code 500`); @@ -1381,7 +1451,11 @@ describe('MirrorNodeClient', async function () { mock.onGet(transactionPath(invalidAddress, 1)).reply(400, null); let errorRaised = false; try { - await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp(invalidAddress, timestamp); + await mirrorNodeInstance.getAccountLatestEthereumTransactionsByTimestamp( + invalidAddress, + timestamp, + requestDetails, + ); } catch (error: any) { errorRaised = true; expect(error.message).to.equal(`Request failed with status code 400`); @@ -1396,24 +1470,24 @@ describe('MirrorNodeClient', async function () { it('should return false for contract for non existing contract', async () => { mock.onGet(contractPath).reply(404, mockData.notFound); - const isValid = await mirrorNodeInstance.isValidContract(evmAddress); + const isValid = await mirrorNodeInstance.isValidContract(evmAddress, requestDetails); expect(isValid).to.be.false; }); it('should return valid for contract for existing contract', async () => { mock.onGet(contractPath).reply(200, mockData.contract); - const isValid = await mirrorNodeInstance.isValidContract(evmAddress); + const isValid = await mirrorNodeInstance.isValidContract(evmAddress, requestDetails); expect(isValid).to.be.true; }); it('should return valid for contract from cache on additional calls', async () => { mock.onGet(contractPath).reply(200, mockData.contract); - let isValid = await mirrorNodeInstance.isValidContract(evmAddress); + let isValid = await mirrorNodeInstance.isValidContract(evmAddress, requestDetails); expect(isValid).to.be.true; // verify that the cache is used mock.onGet(contractPath).reply(404, mockData.notFound); - isValid = await mirrorNodeInstance.isValidContract(evmAddress); + isValid = await mirrorNodeInstance.isValidContract(evmAddress, requestDetails); expect(isValid).to.be.true; }); }); @@ -1424,20 +1498,20 @@ describe('MirrorNodeClient', async function () { it('should fail to fetch contract for non existing contract', async () => { mock.onGet(contractPath).reply(404, mockData.notFound); - const id = await mirrorNodeInstance.getContractId(evmAddress); + const id = await mirrorNodeInstance.getContractId(evmAddress, requestDetails); expect(id).to.not.exist; }); it('should fetch id for existing contract', async () => { mock.onGet(contractPath).reply(200, mockData.contract); - const id = await mirrorNodeInstance.getContractId(evmAddress); + const id = await mirrorNodeInstance.getContractId(evmAddress, requestDetails); expect(id).to.exist; expect(id).to.be.equal(mockData.contract.contract_id); }); it('should fetch contract for existing contract from cache on additional calls', async () => { mock.onGet(contractPath).reply(200, mockData.contract); - let id = await mirrorNodeInstance.getContractId(evmAddress); + let id = await mirrorNodeInstance.getContractId(evmAddress, requestDetails); expect(id).to.exist; expect(id).to.be.equal(mockData.contract.contract_id); @@ -1453,26 +1527,26 @@ describe('MirrorNodeClient', async function () { it('should fail to fetch blocks for empty network', async () => { mock.onGet(blockPath).reply(404, mockData.notFound); - const earlierBlock = await mirrorNodeInstance.getEarliestBlock(); + const earlierBlock = await mirrorNodeInstance.getEarliestBlock(requestDetails); expect(earlierBlock).to.not.exist; }); it('should fetch block for existing valid network', async () => { mock.onGet(blockPath).reply(200, { blocks: [mockData.blocks.blocks[0]] }); - const earlierBlock = await mirrorNodeInstance.getEarliestBlock(); + const earlierBlock = await mirrorNodeInstance.getEarliestBlock(requestDetails); expect(earlierBlock).to.exist; expect(earlierBlock.name).to.be.equal(mockData.blocks.blocks[0].name); }); it('should fetch block for valid network from cache on additional calls', async () => { mock.onGet(blockPath).reply(200, { blocks: [mockData.blocks.blocks[0]] }); - let earlierBlock = await mirrorNodeInstance.getEarliestBlock(); + let earlierBlock = await mirrorNodeInstance.getEarliestBlock(requestDetails); expect(earlierBlock).to.exist; expect(earlierBlock.name).to.be.equal(mockData.blocks.blocks[0].name); // verify that the cache is used mock.onGet(blockPath).reply(404, mockData.notFound); - earlierBlock = await mirrorNodeInstance.getEarliestBlock(); + earlierBlock = await mirrorNodeInstance.getEarliestBlock(requestDetails); expect(earlierBlock).to.exist; expect(earlierBlock.name).to.be.equal(mockData.blocks.blocks[0].name); }); diff --git a/packages/relay/tests/lib/openrpc.spec.ts b/packages/relay/tests/lib/openrpc.spec.ts index a245b4a806..b264a40044 100644 --- a/packages/relay/tests/lib/openrpc.spec.ts +++ b/packages/relay/tests/lib/openrpc.spec.ts @@ -29,12 +29,13 @@ import axios from 'axios'; import sinon from 'sinon'; import dotenv from 'dotenv'; import MockAdapter from 'axios-mock-adapter'; -import { RelayImpl } from '../../src/lib/relay'; import { Registry } from 'prom-client'; +import { BigNumber } from 'bignumber.js'; +import { RelayImpl } from '../../src'; import { EthImpl } from '../../src/lib/eth'; -import { SDKClient } from '../../src/lib/clients'; -import { MirrorNodeClient } from '../../src/lib/clients/mirrorNodeClient'; +import { SDKClient, MirrorNodeClient } from '../../src/lib/clients'; +import { RequestDetails } from '../../src/lib/types'; import openRpcSchema from '../../../../docs/openrpc.json'; import { @@ -63,19 +64,20 @@ import { defaultLogTopics, defaultNetworkFees, defaultTxHash, - getRequestId, signedTransactionHash, } from '../helpers'; import { NOT_FOUND_RES } from './eth/eth-config'; import ClientService from '../../src/lib/services/hapiService/hapiService'; import HbarLimit from '../../src/lib/hbarlimiter'; -import { numberTo0x } from '../../../../packages/relay/src/formatters'; +import { numberTo0x } from '../../src/formatters'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); import constants from '../../src/lib/constants'; import { CacheService } from '../../src/lib/services/cacheService/cacheService'; import EventEmitter from 'events'; +import Long from 'long'; +import { AccountInfo } from '@hashgraph/sdk'; process.env.npm_package_version = 'relay/0.0.1-SNAPSHOT'; @@ -86,7 +88,7 @@ const Relay = new RelayImpl(logger, registry); let mock: MockAdapter; let mirrorNodeInstance: MirrorNodeClient; let clientServiceInstance: ClientService; -let sdkClientStub: any; +let sdkClientStub: sinon.SinonStubbedInstance; const noTransactions = '?transactions=false'; @@ -95,6 +97,8 @@ describe('Open RPC Specification', function () { let methodsResponseSchema: { [method: string]: any }; let ethImpl: EthImpl; + const requestDetails = new RequestDetails({ requestId: 'testId', ipAddress: '0.0.0.0' }); + this.beforeAll(async () => { rpcDocument = await parseOpenRPCDocument(JSON.stringify(openRpcSchema)); methodsResponseSchema = rpcDocument.methods.reduce( @@ -206,11 +210,11 @@ describe('Open RPC Specification', function () { mock.onGet(`contracts/${log.address}`).reply(200, defaultContract); } mock.onPost(`contracts/call`, { ...defaultCallData, estimate: false }).reply(200, { result: '0x12' }); - sdkClientStub.getAccountBalanceInWeiBar.returns(1000); - sdkClientStub.getAccountBalanceInTinyBar.returns(100000000000); - sdkClientStub.getContractByteCode.returns(Buffer.from(bytecode.replace('0x', ''), 'hex')); - sdkClientStub.getAccountInfo.returns({ ethereumNonce: '0x1' }); - sdkClientStub.submitEthereumTransaction.returns({}); + sdkClientStub.getAccountBalanceInWeiBar.resolves(BigNumber(1000)); + sdkClientStub.getAccountBalanceInTinyBar.resolves(BigNumber(100000000000)); + sdkClientStub.getContractByteCode.resolves(Buffer.from(bytecode.replace('0x', ''), 'hex')); + sdkClientStub.getAccountInfo.resolves({ ethereumNonce: Long.ONE } as unknown as AccountInfo); + sdkClientStub.submitEthereumTransaction.resolves(); mock.onGet(`accounts/${defaultContractResults.results[0].from}?transactions=false`).reply(200); mock.onGet(`accounts/${defaultContractResults.results[1].from}?transactions=false`).reply(200); mock.onGet(`accounts/${defaultContractResults.results[0].to}?transactions=false`).reply(200); @@ -244,112 +248,112 @@ describe('Open RPC Specification', function () { }); it('should execute "eth_accounts"', function () { - const response = ethImpl.accounts(); + const response = ethImpl.accounts(requestDetails); validateResponseSchema(methodsResponseSchema.eth_accounts, response); }); it('should execute "eth_blockNumber"', async function () { - const response = await ethImpl.blockNumber(); + const response = await ethImpl.blockNumber(requestDetails); validateResponseSchema(methodsResponseSchema.eth_blockNumber, response); }); it('should execute "eth_chainId"', function () { - const response = ethImpl.chainId(); + const response = ethImpl.chainId(requestDetails); validateResponseSchema(methodsResponseSchema.eth_chainId, response); }); it('should execute "eth_coinbase"', function () { - const response = ethImpl.coinbase(); + const response = ethImpl.coinbase(requestDetails); validateResponseSchema(methodsResponseSchema.eth_coinbase, response); }); it('should execute "eth_estimateGas"', async function () { mock.onGet(`accounts/undefined${noTransactions}`).reply(404); - const response = await ethImpl.estimateGas({}, null); + const response = await ethImpl.estimateGas({}, null, requestDetails); validateResponseSchema(methodsResponseSchema.eth_estimateGas, response); }); it('should execute "eth_feeHistory"', async function () { - const response = await ethImpl.feeHistory(1, 'latest', [0]); + const response = await ethImpl.feeHistory(1, 'latest', [0], requestDetails); validateResponseSchema(methodsResponseSchema.eth_feeHistory, response); }); it('should execute "eth_gasPrice"', async function () { - const response = await ethImpl.gasPrice(); + const response = await ethImpl.gasPrice(requestDetails); validateResponseSchema(methodsResponseSchema.eth_gasPrice, response); }); it('should execute "eth_getBalance"', async function () { - const response = await ethImpl.getBalance(contractAddress1, 'latest', getRequestId()); + const response = await ethImpl.getBalance(contractAddress1, 'latest', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBalance, response); }); it('should execute "eth_getBlockByHash" with hydrated = true', async function () { - const response = await ethImpl.getBlockByHash(blockHash, true); + const response = await ethImpl.getBlockByHash(blockHash, true, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockByHash, response); }); it('should execute "eth_getBlockByHash" with hydrated = false', async function () { - const response = await ethImpl.getBlockByHash(blockHash, true); + const response = await ethImpl.getBlockByHash(blockHash, true, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockByHash, response); }); it('should execute "eth_getBlockByNumber" with hydrated = true', async function () { - const response = await ethImpl.getBlockByNumber(numberTo0x(blockNumber), true); + const response = await ethImpl.getBlockByNumber(numberTo0x(blockNumber), true, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockByNumber, response); }); it('should execute "eth_getBlockByNumber" with hydrated = false', async function () { - const response = await ethImpl.getBlockByNumber(numberTo0x(blockNumber), false); + const response = await ethImpl.getBlockByNumber(numberTo0x(blockNumber), false, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockByNumber, response); }); it('should execute "eth_getBlockTransactionCountByHash"', async function () { - const response = await ethImpl.getBlockTransactionCountByHash(blockHash); + const response = await ethImpl.getBlockTransactionCountByHash(blockHash, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockTransactionCountByHash, response); }); it('should execute "eth_getBlockTransactionCountByNumber" with block tag', async function () { - const response = await ethImpl.getBlockTransactionCountByNumber('latest'); + const response = await ethImpl.getBlockTransactionCountByNumber('latest', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockTransactionCountByNumber, response); }); it('should execute "eth_getBlockTransactionCountByNumber" with block number', async function () { - const response = await ethImpl.getBlockTransactionCountByNumber('0x3'); + const response = await ethImpl.getBlockTransactionCountByNumber('0x3', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getBlockTransactionCountByNumber, response); }); it('should execute "eth_getCode" with block tag', async function () { mock.onGet(`tokens/${defaultContractResults.results[0].contract_id}`).reply(404); - const response = await ethImpl.getCode(contractAddress1, 'latest'); + const response = await ethImpl.getCode(contractAddress1, 'latest', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getCode, response); }); it('should execute "eth_getCode" with block number', async function () { mock.onGet(`tokens/${defaultContractResults.results[0].contract_id}`).reply(404); - const response = await ethImpl.getCode(contractAddress1, '0x3'); + const response = await ethImpl.getCode(contractAddress1, '0x3', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getCode, response); }); it('should execute "eth_getLogs" with no filters', async function () { - const response = await ethImpl.getLogs(null, 'latest', 'latest', null, null); + const response = await ethImpl.getLogs(null, 'latest', 'latest', null, null, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getLogs, response); }); @@ -374,13 +378,17 @@ describe('Open RPC Specification', function () { mock.onGet(`contracts/${log.address}`).reply(200, defaultContract); } - const response = await ethImpl.getLogs(null, 'latest', 'latest', null, defaultLogTopics); + const response = await ethImpl.getLogs(null, 'latest', 'latest', null, defaultLogTopics, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getLogs, response); }); it('should execute "eth_getTransactionByBlockHashAndIndex"', async function () { - const response = await ethImpl.getTransactionByBlockHashAndIndex(defaultBlock.hash, numberTo0x(defaultBlock.count)); + const response = await ethImpl.getTransactionByBlockHashAndIndex( + defaultBlock.hash, + numberTo0x(defaultBlock.count), + requestDetails, + ); validateResponseSchema(methodsResponseSchema.eth_getTransactionByBlockHashAndIndex, response); }); @@ -389,13 +397,14 @@ describe('Open RPC Specification', function () { const response = await ethImpl.getTransactionByBlockNumberAndIndex( numberTo0x(defaultBlock.number), numberTo0x(defaultBlock.count), + requestDetails, ); validateResponseSchema(methodsResponseSchema.eth_getTransactionByBlockNumberAndIndex, response); }); it('should execute "eth_getTransactionByHash"', async function () { - const response = await ethImpl.getTransactionByHash(defaultTxHash); + const response = await ethImpl.getTransactionByHash(defaultTxHash, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getTransactionByHash, response); }); @@ -405,7 +414,7 @@ describe('Open RPC Specification', function () { .onGet(`accounts/${contractAddress1}${noTransactions}`) .reply(200, { account: contractAddress1, ethereum_nonce: 5 }); mock.onGet(`contracts/${contractAddress1}${noTransactions}`).reply(404); - const response = await ethImpl.getTransactionCount(contractAddress1, 'latest'); + const response = await ethImpl.getTransactionCount(contractAddress1, 'latest', requestDetails); validateResponseSchema(methodsResponseSchema.eth_getTransactionCount, response); }); @@ -414,102 +423,103 @@ describe('Open RPC Specification', function () { mock.onGet(`contracts/${defaultDetailedContractResultByHash.created_contract_ids[0]}`).reply(404); sinon.stub(ethImpl, 'getCurrentGasPriceForBlock').resolves('0xad78ebc5ac620000'); - const response = await ethImpl.getTransactionReceipt(defaultTxHash); + const response = await ethImpl.getTransactionReceipt(defaultTxHash, requestDetails); validateResponseSchema(methodsResponseSchema.eth_getTransactionReceipt, response); }); it('should execute "eth_getUncleByBlockHashAndIndex"', async function () { - const response = await ethImpl.getUncleByBlockHashAndIndex(); + const response = await ethImpl.getUncleByBlockHashAndIndex(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getUncleByBlockHashAndIndex, response); }); it('should execute "eth_getUncleByBlockNumberAndIndex"', async function () { - const response = await ethImpl.getUncleByBlockNumberAndIndex(); + const response = await ethImpl.getUncleByBlockNumberAndIndex(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getUncleByBlockNumberAndIndex, response); }); it('should execute "eth_getUncleByBlockNumberAndIndex"', async function () { - const response = await ethImpl.getUncleByBlockNumberAndIndex(); + const response = await ethImpl.getUncleByBlockNumberAndIndex(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getUncleByBlockNumberAndIndex, response); }); it('should execute "eth_getUncleCountByBlockHash"', async function () { - const response = await ethImpl.getUncleCountByBlockHash(); + const response = await ethImpl.getUncleCountByBlockHash(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getUncleCountByBlockHash, response); }); it('should execute "eth_getUncleCountByBlockNumber"', async function () { - const response = await ethImpl.getUncleCountByBlockNumber(); + const response = await ethImpl.getUncleCountByBlockNumber(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getUncleCountByBlockNumber, response); }); it('should execute "eth_getWork"', async function () { - const response = ethImpl.getWork(); + const response = ethImpl.getWork(requestDetails); validateResponseSchema(methodsResponseSchema.eth_getWork, response); }); it('should execute "eth_hashrate"', async function () { - const response = await ethImpl.hashrate(); + const response = await ethImpl.hashrate(requestDetails); validateResponseSchema(methodsResponseSchema.eth_hashrate, response); }); it('should execute "eth_mining"', async function () { - const response = await ethImpl.mining(); + const response = await ethImpl.mining(requestDetails); validateResponseSchema(methodsResponseSchema.eth_mining, response); }); it('should execute "eth_protocolVersion"', async function () { - const response = ethImpl.protocolVersion(); + const response = ethImpl.protocolVersion(requestDetails); validateResponseSchema(methodsResponseSchema.eth_protocolVersion, response); }); it('should execute "eth_sendRawTransaction"', async function () { - const response = await ethImpl.sendRawTransaction(signedTransactionHash, getRequestId()); + const response = await ethImpl.sendRawTransaction(signedTransactionHash, requestDetails); + validateResponseSchema(methodsResponseSchema.eth_sendRawTransaction, response); }); it('should execute "eth_sendTransaction"', async function () { - const response = ethImpl.sendTransaction(); + const response = ethImpl.sendTransaction(requestDetails); validateResponseSchema(methodsResponseSchema.eth_sendTransaction, response); }); it('should execute "eth_signTransaction"', async function () { - const response = ethImpl.signTransaction(); + const response = ethImpl.signTransaction(requestDetails); validateResponseSchema(methodsResponseSchema.eth_signTransaction, response); }); it('should execute "eth_sign"', async function () { - const response = ethImpl.sign(); + const response = ethImpl.sign(requestDetails); validateResponseSchema(methodsResponseSchema.eth_sign, response); }); it('should execute "eth_submitHashrate"', async function () { - const response = ethImpl.submitHashrate(); + const response = ethImpl.submitHashrate(requestDetails); validateResponseSchema(methodsResponseSchema.eth_submitHashrate, response); }); it('should execute "eth_submitWork"', async function () { - const response = await ethImpl.submitWork(); + const response = await ethImpl.submitWork(requestDetails); validateResponseSchema(methodsResponseSchema.eth_submitWork, response); }); it('should execute "eth_syncing"', async function () { - const response = await ethImpl.syncing(); + const response = await ethImpl.syncing(requestDetails); validateResponseSchema(methodsResponseSchema.eth_syncing, response); }); diff --git a/packages/relay/tests/lib/precheck.spec.ts b/packages/relay/tests/lib/precheck.spec.ts index ceadf43e47..f316531c22 100644 --- a/packages/relay/tests/lib/precheck.spec.ts +++ b/packages/relay/tests/lib/precheck.spec.ts @@ -34,12 +34,14 @@ import constants from '../../src/lib/constants'; import { JsonRpcError, predefined } from '../../src'; import { CacheService } from '../../src/lib/services/cacheService/cacheService'; import { ONE_TINYBAR_IN_WEI_HEX } from './eth/eth-config'; +import { RequestDetails } from '../../src/lib/types'; const logger = pino(); const limitOrderPostFix = '?order=desc&limit=1'; const transactionsPostFix = '?transactions=false'; describe('Precheck', async function () { + const requestDetails = new RequestDetails({ requestId: 'precheckTest', ipAddress: '0.0.0.0' }); const txWithMatchingChainId = '0x02f87482012a0485a7a358200085a7a3582000832dc6c09400000000000000000000000000000000000003f78502540be40080c001a006f4cd8e6f84b76a05a5c1542a08682c928108ef7163d9c1bf1f3b636b1cd1fba032097cbf2dda17a2dcc40f62c97964d9d930cdce2e8a9df9a8ba023cda28e4ad'; const parsedTxWithMatchingChainId = ethers.Transaction.from(txWithMatchingChainId); @@ -141,7 +143,7 @@ describe('Precheck', async function () { describe('chainId', async function () { it('should pass for matching chainId', async function () { try { - precheck.chainId(parsedTxWithMatchingChainId); + precheck.chainId(parsedTxWithMatchingChainId, requestDetails); } catch (e: any) { expect(e).to.not.exist; } @@ -149,7 +151,7 @@ describe('Precheck', async function () { it('should pass when chainId=0x0', async function () { try { - precheck.chainId(parsedtxWithChainId0x0); + precheck.chainId(parsedtxWithChainId0x0, requestDetails); } catch (e: any) { expect(e).to.not.exist; } @@ -157,7 +159,7 @@ describe('Precheck', async function () { it('should not pass for non-matching chainId', async function () { try { - precheck.chainId(parsedTxWithNonMatchingChainId); + precheck.chainId(parsedTxWithNonMatchingChainId, requestDetails); expectedError(); } catch (e: any) { expect(e).to.exist; @@ -191,7 +193,7 @@ describe('Precheck', async function () { ? `Transaction gas limit '${gasLimit}' exceeds max gas per sec limit '${constants.MAX_GAS_PER_SEC}'` : `Transaction gas limit provided '${gasLimit}' is insufficient of intrinsic gas required `; try { - await precheck.gasLimit(parsedTx); + await precheck.gasLimit(parsedTx, requestDetails); expectedError(); } catch (e: any) { console.log(e); @@ -214,7 +216,7 @@ describe('Precheck', async function () { const parsedTx = ethers.Transaction.from(signed); try { - await precheck.gasLimit(parsedTx); + precheck.gasLimit(parsedTx, requestDetails); } catch (e: any) { expect(e).to.not.exist; } @@ -243,12 +245,12 @@ describe('Precheck', async function () { }); it('should pass for gas price gt to required gas price', async function () { - const result = precheck.gasPrice(parsedTxWithMatchingChainId, 10); + const result = precheck.gasPrice(parsedTxWithMatchingChainId, 10, requestDetails); expect(result).to.not.exist; }); it('should pass for gas price equal to required gas price', async function () { - const result = precheck.gasPrice(parsedTxWithMatchingChainId, defaultGasPrice); + const result = precheck.gasPrice(parsedTxWithMatchingChainId, defaultGasPrice, requestDetails); expect(result).to.not.exist; }); @@ -273,6 +275,7 @@ describe('Precheck', async function () { const result = precheck.gasPrice( parsedDeterministicDeploymentTransaction, 100 * constants.TINYBAR_TO_WEIBAR_COEF, + requestDetails, ); expect(result).to.not.exist; }); @@ -280,7 +283,7 @@ describe('Precheck', async function () { it('should not pass for gas price not enough', async function () { const minGasPrice = 1000 * constants.TINYBAR_TO_WEIBAR_COEF; try { - precheck.gasPrice(parsedTxWithMatchingChainId, minGasPrice); + precheck.gasPrice(parsedTxWithMatchingChainId, minGasPrice, requestDetails); expectedError(); } catch (e: any) { expect(e).to.exist; @@ -292,7 +295,7 @@ describe('Precheck', async function () { it('should pass for gas price not enough but within buffer', async function () { const adjustedGasPrice = parsedTxGasPrice + Number(constants.GAS_PRICE_TINY_BAR_BUFFER); - precheck.gasPrice(parsedTxWithMatchingChainId, adjustedGasPrice); + precheck.gasPrice(parsedTxWithMatchingChainId, adjustedGasPrice, requestDetails); }); }); @@ -312,7 +315,7 @@ describe('Precheck', async function () { }; try { - await precheck.balance(parsedTransaction, account); + precheck.balance(parsedTransaction, account, requestDetails); expectedError(); } catch (e: any) { expect(e).to.exist; @@ -325,7 +328,7 @@ describe('Precheck', async function () { const account = null; try { - await precheck.balance(parsedTransaction, account); + precheck.balance(parsedTransaction, account, requestDetails); expectedError(); } catch (e: any) { expect(e).to.exist; @@ -342,7 +345,7 @@ describe('Precheck', async function () { }, }; - const result = await precheck.balance(parsedTransaction, account); + const result = precheck.balance(parsedTransaction, account, requestDetails); expect(result).to.not.exist; }); @@ -354,7 +357,7 @@ describe('Precheck', async function () { }, }; - const result = await precheck.balance(parsedTransaction, account); + const result = precheck.balance(parsedTransaction, account, requestDetails); expect(result).to.not.exist; }); @@ -366,7 +369,7 @@ describe('Precheck', async function () { }, }; - const result = await precheck.balance(parsedTransaction, account); + const result = precheck.balance(parsedTransaction, account, requestDetails); expect(result).to.not.exist; }); @@ -378,7 +381,7 @@ describe('Precheck', async function () { }, }; - const result = await precheck.balance(parsedTransaction, account); + const result = precheck.balance(parsedTransaction, account, requestDetails); expect(result).to.not.exist; }); @@ -390,7 +393,7 @@ describe('Precheck', async function () { }, }; - const result = await precheck.balance(parsedTransaction, account); + const result = precheck.balance(parsedTransaction, account, requestDetails); expect(result).to.not.exist; }); }); @@ -412,7 +415,7 @@ describe('Precheck', async function () { mock.onGet(`accounts/${parsedTx.from}${limitOrderPostFix}`).reply(200, mirrorAccount); try { - await precheck.nonce(parsedTx, mirrorAccount.ethereum_nonce); + precheck.nonce(parsedTx, mirrorAccount.ethereum_nonce, requestDetails); expectedError(); } catch (e: any) { expect(e).to.eql(predefined.NONCE_TOO_LOW(parsedTx.nonce, mirrorAccount.ethereum_nonce)); @@ -429,7 +432,7 @@ describe('Precheck', async function () { mock.onGet(`accounts/${parsedTx.from}${limitOrderPostFix}`).reply(200, mirrorAccount); - await precheck.nonce(parsedTx, mirrorAccount.ethereum_nonce); + precheck.nonce(parsedTx, mirrorAccount.ethereum_nonce, requestDetails); }); }); @@ -450,9 +453,8 @@ describe('Precheck', async function () { it(`should fail for missing account`, async function () { mock.onGet(`accounts/${parsedTx.from}${transactionsPostFix}`).reply(404, mockData.notFound); - try { - await precheck.verifyAccount(parsedTx); + await precheck.verifyAccount(parsedTx, requestDetails); expectedError(); } catch (e: any) { expect(e).to.exist; @@ -463,7 +465,7 @@ describe('Precheck', async function () { it(`should not fail for matched account`, async function () { mock.onGet(`accounts/${parsedTx.from}${transactionsPostFix}`).reply(200, mirrorAccount); - const account = await precheck.verifyAccount(parsedTx); + const account = await precheck.verifyAccount(parsedTx, requestDetails); expect(account.ethereum_nonce).to.eq(defaultNonce); }); @@ -573,7 +575,7 @@ describe('Precheck', async function () { it('should accept legacy transactions', async () => { const signedLegacy = await signTransaction(defaultTx); - expect(precheck.transactionType(ethers.Transaction.from(signedLegacy))).not.to.throw; + expect(() => precheck.transactionType(ethers.Transaction.from(signedLegacy), requestDetails)).not.to.throw; }); it('should accept London transactions', async () => { @@ -583,7 +585,7 @@ describe('Precheck', async function () { maxPriorityFeePerGas: defaultGasPrice, maxFeePerGas: defaultGasPrice, }); - expect(precheck.transactionType(ethers.Transaction.from(signedLondon))).not.to.throw; + expect(() => precheck.transactionType(ethers.Transaction.from(signedLondon), requestDetails)).not.to.throw; }); it('should reject Cancun transactions', async () => { @@ -595,7 +597,7 @@ describe('Precheck', async function () { maxFeePerBlobGas: defaultGasPrice, blobVersionedHashes: [blobVersionedHash], }); - precheck.transactionType(ethers.Transaction.from(signedCancun)); + precheck.transactionType(ethers.Transaction.from(signedCancun), requestDetails); } catch (e) { error = e; } diff --git a/packages/relay/tests/lib/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.spec.ts b/packages/relay/tests/lib/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.spec.ts index 29d8cffc04..5b128ece92 100644 --- a/packages/relay/tests/lib/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.spec.ts +++ b/packages/relay/tests/lib/repositories/hbarLimiter/ethAddressHbarSpendingPlanRepository.spec.ts @@ -28,12 +28,17 @@ import { EthAddressHbarSpendingPlanNotFoundError } from '../../../../src/lib/db/ import { randomBytes, uuidV4 } from 'ethers'; import { Registry } from 'prom-client'; import { useInMemoryRedisServer } from '../../../helpers'; +import { RequestDetails } from '../../../../dist/lib/types'; chai.use(chaiAsPromised); describe('EthAddressHbarSpendingPlanRepository', function () { const logger = pino(); const registry = new Registry(); + const requestDetails = new RequestDetails({ + requestId: 'ethAddressHbarSpendingPlanRepositoryTest', + ipAddress: '0.0.0.0', + }); const tests = (isSharedCacheEnabled: boolean) => { let cacheService: CacheService; @@ -69,15 +74,15 @@ describe('EthAddressHbarSpendingPlanRepository', function () { it('retrieves an address plan by address', async () => { const ethAddress = '0x123'; const addressPlan: IEthAddressHbarSpendingPlan = { ethAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test', requestDetails); - const result = await repository.findByAddress(ethAddress); + const result = await repository.findByAddress(ethAddress, requestDetails); expect(result).to.deep.equal(addressPlan); }); it('throws an error if address plan is not found', async () => { const ethAddress = '0xnonexistent'; - await expect(repository.findByAddress(ethAddress)).to.be.eventually.rejectedWith( + await expect(repository.findByAddress(ethAddress, requestDetails)).to.be.eventually.rejectedWith( EthAddressHbarSpendingPlanNotFoundError, `EthAddressHbarSpendingPlan with address ${ethAddress} not found`, ); @@ -89,10 +94,11 @@ describe('EthAddressHbarSpendingPlanRepository', function () { const ethAddress = '0x123'; const addressPlan: IEthAddressHbarSpendingPlan = { ethAddress, planId: uuidV4(randomBytes(16)) }; - await repository.save(addressPlan); + await repository.save(addressPlan, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ethAddress}`, 'test', + requestDetails, ); expect(result).to.deep.equal(addressPlan); }); @@ -100,14 +106,15 @@ describe('EthAddressHbarSpendingPlanRepository', function () { it('overwrites an existing address plan', async () => { const ethAddress = '0x123'; const addressPlan: IEthAddressHbarSpendingPlan = { ethAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test', requestDetails); const newPlanId = uuidV4(randomBytes(16)); const newAddressPlan: IEthAddressHbarSpendingPlan = { ethAddress, planId: newPlanId }; - await repository.save(newAddressPlan); + await repository.save(newAddressPlan, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ethAddress}`, 'test', + requestDetails, ); expect(result).to.deep.equal(newAddressPlan); }); @@ -117,19 +124,20 @@ describe('EthAddressHbarSpendingPlanRepository', function () { it('deletes an address plan successfully', async () => { const ethAddress = '0x123'; const addressPlan: IEthAddressHbarSpendingPlan = { ethAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ethAddress}`, addressPlan, 'test', requestDetails); - await repository.delete(ethAddress); + await repository.delete(ethAddress, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ethAddress}`, 'test', + requestDetails, ); expect(result).to.be.null; }); it('does not throw an error if address plan to delete does not exist', async () => { const ethAddress = '0xnonexistent'; - await expect(repository.delete(ethAddress)).to.be.fulfilled; + await expect(repository.delete(ethAddress, requestDetails)).to.be.fulfilled; }); }); }; diff --git a/packages/relay/tests/lib/repositories/hbarLimiter/hbarSpendingPlanRepository.spec.ts b/packages/relay/tests/lib/repositories/hbarLimiter/hbarSpendingPlanRepository.spec.ts index df230cfa13..3daa0883a0 100644 --- a/packages/relay/tests/lib/repositories/hbarLimiter/hbarSpendingPlanRepository.spec.ts +++ b/packages/relay/tests/lib/repositories/hbarLimiter/hbarSpendingPlanRepository.spec.ts @@ -33,12 +33,14 @@ import { IHbarSpendingRecord } from '../../../../src/lib/db/types/hbarLimiter/hb import { SubscriptionType } from '../../../../src/lib/db/types/hbarLimiter/subscriptionType'; import { IDetailedHbarSpendingPlan } from '../../../../src/lib/db/types/hbarLimiter/hbarSpendingPlan'; import { useInMemoryRedisServer } from '../../../helpers'; +import { RequestDetails } from '../../../../src/lib/types'; chai.use(chaiAsPromised); describe('HbarSpendingPlanRepository', function () { const logger = pino(); const registry = new Registry(); + const requestDetails = new RequestDetails({ requestId: 'hbarSpendingPlanRepositoryTest', ipAddress: '0.0.0.0' }); const tests = (isSharedCacheEnabled: boolean) => { let cacheService: CacheService; @@ -65,20 +67,22 @@ describe('HbarSpendingPlanRepository', function () { } afterEach(async () => { - await cacheService.clear(); + await cacheService.clear(requestDetails); }); describe('create', () => { it('creates a plan successfully', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - await expect(repository.findByIdWithDetails(createdPlan.id)).to.be.eventually.deep.equal(createdPlan); + const createdPlan = await repository.create(subscriptionType, requestDetails); + await expect(repository.findByIdWithDetails(createdPlan.id, requestDetails)).to.be.eventually.deep.equal( + createdPlan, + ); }); }); describe('findById', () => { it('throws an error if plan is not found by ID', async () => { - await expect(repository.findById('non-existent-id')).to.be.eventually.rejectedWith( + await expect(repository.findById('non-existent-id', requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID non-existent-id not found`, ); @@ -86,14 +90,14 @@ describe('HbarSpendingPlanRepository', function () { it('returns a plan by ID', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - await expect(repository.findById(createdPlan.id)).to.be.eventually.deep.equal(createdPlan); + const createdPlan = await repository.create(subscriptionType, requestDetails); + await expect(repository.findById(createdPlan.id, requestDetails)).to.be.eventually.deep.equal(createdPlan); }); }); describe('findByIdWithDetails', () => { it('throws an error if plan is not found by ID', async () => { - await expect(repository.findByIdWithDetails('non-existent-id')).to.be.eventually.rejectedWith( + await expect(repository.findByIdWithDetails('non-existent-id', requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID non-existent-id not found`, ); @@ -101,14 +105,16 @@ describe('HbarSpendingPlanRepository', function () { it('returns a plan by ID', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - await expect(repository.findByIdWithDetails(createdPlan.id)).to.be.eventually.deep.equal(createdPlan); + const createdPlan = await repository.create(subscriptionType, requestDetails); + await expect(repository.findByIdWithDetails(createdPlan.id, requestDetails)).to.be.eventually.deep.equal( + createdPlan, + ); }); }); describe('getSpendingHistory', () => { it('throws an error if plan not found by ID', async () => { - await expect(repository.getSpendingHistory('non-existent-id')).to.be.eventually.rejectedWith( + await expect(repository.getSpendingHistory('non-existent-id', requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID non-existent-id not found`, ); @@ -116,20 +122,20 @@ describe('HbarSpendingPlanRepository', function () { it('returns an empty array if spending history is empty', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - const spendingHistory = await repository.getSpendingHistory(createdPlan.id); + const createdPlan = await repository.create(subscriptionType, requestDetails); + const spendingHistory = await repository.getSpendingHistory(createdPlan.id, requestDetails); expect(spendingHistory).to.deep.equal([]); }); it('retrieves spending history for a plan', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); const key = `${repository['collectionKey']}:${createdPlan.id}:spendingHistory`; const hbarSpending = { amount: 100, timestamp: new Date() } as IHbarSpendingRecord; - await cacheService.rPush(key, hbarSpending, 'test'); + await cacheService.rPush(key, hbarSpending, 'test', requestDetails); - const spendingHistory = await repository.getSpendingHistory(createdPlan.id); + const spendingHistory = await repository.getSpendingHistory(createdPlan.id, requestDetails); expect(spendingHistory).to.have.lengthOf(1); expect(spendingHistory[0].amount).to.equal(hbarSpending.amount); expect(spendingHistory[0].timestamp).to.be.a('Date'); @@ -139,13 +145,13 @@ describe('HbarSpendingPlanRepository', function () { describe('addAmountToSpendingHistory', () => { it('adds amount to spending history', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - await expect(repository.getSpendingHistory(createdPlan.id)).to.be.eventually.deep.equal([]); + const createdPlan = await repository.create(subscriptionType, requestDetails); + await expect(repository.getSpendingHistory(createdPlan.id, requestDetails)).to.be.eventually.deep.equal([]); const amount = 100; - await repository.addAmountToSpendingHistory(createdPlan.id, amount); + await repository.addAmountToSpendingHistory(createdPlan.id, amount, requestDetails); - const spendingHistory = await repository.getSpendingHistory(createdPlan.id); + const spendingHistory = await repository.getSpendingHistory(createdPlan.id, requestDetails); expect(spendingHistory).to.have.lengthOf(1); expect(spendingHistory[0].amount).to.equal(amount); expect(spendingHistory[0].timestamp).to.be.a('Date'); @@ -153,15 +159,15 @@ describe('HbarSpendingPlanRepository', function () { it('adds multiple amounts to spending history', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); - await expect(repository.getSpendingHistory(createdPlan.id)).to.be.eventually.deep.equal([]); + const createdPlan = await repository.create(subscriptionType, requestDetails); + await expect(repository.getSpendingHistory(createdPlan.id, requestDetails)).to.be.eventually.deep.equal([]); const amounts = [100, 200, 300]; for (const amount of amounts) { - await repository.addAmountToSpendingHistory(createdPlan.id, amount); + await repository.addAmountToSpendingHistory(createdPlan.id, amount, requestDetails); } - const spendingHistory = await repository.getSpendingHistory(createdPlan.id); + const spendingHistory = await repository.getSpendingHistory(createdPlan.id, requestDetails); expect(spendingHistory).to.have.lengthOf(3); expect(spendingHistory.map((entry) => entry.amount)).to.deep.equal(amounts); }); @@ -170,7 +176,7 @@ describe('HbarSpendingPlanRepository', function () { const id = 'non-existent-id'; const amount = 100; - await expect(repository.addAmountToSpendingHistory(id, amount)).to.be.eventually.rejectedWith( + await expect(repository.addAmountToSpendingHistory(id, amount, requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID ${id} not found`, ); @@ -197,34 +203,34 @@ describe('HbarSpendingPlanRepository', function () { it('retrieves spent today for a plan', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); const amount = 50; - await repository.addAmountToSpentToday(createdPlan.id, amount); + await repository.addAmountToSpentToday(createdPlan.id, amount, requestDetails); - const spentToday = await repository.getSpentToday(createdPlan.id); + const spentToday = await repository.getSpentToday(createdPlan.id, requestDetails); expect(spentToday).to.equal(amount); }); it('returns 0 if spent today key does not exist', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); - const spentToday = await repository.getSpentToday(createdPlan.id); + const spentToday = await repository.getSpentToday(createdPlan.id, requestDetails); expect(spentToday).to.equal(0); }); it('should expire spent today key at the end of the day', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); const amount = 50; - await repository.addAmountToSpentToday(createdPlan.id, amount); - await expect(repository.getSpentToday(createdPlan.id)).to.eventually.equal(amount); + await repository.addAmountToSpentToday(createdPlan.id, amount, requestDetails); + await expect(repository.getSpentToday(createdPlan.id, requestDetails)).to.eventually.equal(amount); await new Promise((resolve) => setTimeout(resolve, mockedOneDayInMillis + 100)); - await expect(repository.getSpentToday(createdPlan.id)).to.eventually.equal(0); + await expect(repository.getSpentToday(createdPlan.id, requestDetails)).to.eventually.equal(0); }); }); @@ -232,42 +238,42 @@ describe('HbarSpendingPlanRepository', function () { it('resets all spent today entries', async () => { const plans: IDetailedHbarSpendingPlan[] = []; for (const subscriptionType of Object.values(SubscriptionType)) { - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); plans.push(createdPlan); const amount = 50 * plans.length; - await repository.addAmountToSpentToday(createdPlan.id, amount); - await expect(repository.getSpentToday(createdPlan.id)).to.eventually.equal(amount); + await repository.addAmountToSpentToday(createdPlan.id, amount, requestDetails); + await expect(repository.getSpentToday(createdPlan.id, requestDetails)).to.eventually.equal(amount); } - await repository.resetAllSpentTodayEntries(); + await repository.resetAllSpentTodayEntries(requestDetails); for (const plan of plans) { - await expect(repository.getSpentToday(plan.id)).to.eventually.equal(0); + await expect(repository.getSpentToday(plan.id, requestDetails)).to.eventually.equal(0); } }); it('does not throw an error if no spent today keys exist', async () => { - await expect(repository.resetAllSpentTodayEntries()).to.not.be.rejected; + await expect(repository.resetAllSpentTodayEntries(requestDetails)).to.not.be.rejected; }); }); describe('addAmountToSpentToday', () => { it('adds amount to spent today', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); const amount = 50; - await repository.addAmountToSpentToday(createdPlan.id, amount); + await repository.addAmountToSpentToday(createdPlan.id, amount, requestDetails); - const plan = await repository.findByIdWithDetails(createdPlan.id); + const plan = await repository.findByIdWithDetails(createdPlan.id, requestDetails); expect(plan).to.not.be.null; expect(plan!.spentToday).to.equal(amount); // Add more to spent today const newAmount = 100; - await repository.addAmountToSpentToday(createdPlan.id, newAmount); + await repository.addAmountToSpentToday(createdPlan.id, newAmount, requestDetails); - const updatedPlan = await repository.findByIdWithDetails(createdPlan.id); + const updatedPlan = await repository.findByIdWithDetails(createdPlan.id, requestDetails); expect(updatedPlan).to.not.be.null; expect(updatedPlan!.spentToday).to.equal(amount + newAmount); }); @@ -276,7 +282,7 @@ describe('HbarSpendingPlanRepository', function () { const id = 'non-existent-id'; const amount = 50; - await expect(repository.addAmountToSpentToday(id, amount)).to.be.eventually.rejectedWith( + await expect(repository.addAmountToSpentToday(id, amount, requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID ${id} not found`, ); @@ -284,14 +290,16 @@ describe('HbarSpendingPlanRepository', function () { it('throws an error if plan is not active when adding to spent today', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); // Manually set the plan to inactive const key = `${repository['collectionKey']}:${createdPlan.id}`; - await cacheService.set(key, { ...createdPlan, active: false }, 'test'); + await cacheService.set(key, { ...createdPlan, active: false }, 'test', requestDetails); const amount = 50; - await expect(repository.addAmountToSpentToday(createdPlan.id, amount)).to.be.eventually.rejectedWith( + await expect( + repository.addAmountToSpentToday(createdPlan.id, amount, requestDetails), + ).to.be.eventually.rejectedWith( HbarSpendingPlanNotActiveError, `HbarSpendingPlan with ID ${createdPlan.id} is not active`, ); @@ -301,7 +309,7 @@ describe('HbarSpendingPlanRepository', function () { describe('checkExistsAndActive', () => { it('throws error if plan does not exist when checking if exists and active', async () => { const id = 'non-existent-id'; - await expect(repository.checkExistsAndActive(id)).to.be.eventually.rejectedWith( + await expect(repository.checkExistsAndActive(id, requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotFoundError, `HbarSpendingPlan with ID ${id} not found`, ); @@ -309,13 +317,13 @@ describe('HbarSpendingPlanRepository', function () { it('throws error if plan is not active when checking if exists and active', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan = await repository.create(subscriptionType); + const createdPlan = await repository.create(subscriptionType, requestDetails); // Manually set the plan to inactive const key = `${repository['collectionKey']}:${createdPlan.id}`; - await cacheService.set(key, { ...createdPlan, active: false }, 'test'); + await cacheService.set(key, { ...createdPlan, active: false }, 'test', requestDetails); - await expect(repository.checkExistsAndActive(createdPlan.id)).to.be.eventually.rejectedWith( + await expect(repository.checkExistsAndActive(createdPlan.id, requestDetails)).to.be.eventually.rejectedWith( HbarSpendingPlanNotActiveError, `HbarSpendingPlan with ID ${createdPlan.id} is not active`, ); @@ -325,47 +333,56 @@ describe('HbarSpendingPlanRepository', function () { describe('findAllActiveBySubscriptionType', () => { it('returns an empty array if no active plans exist for the subscription type', async () => { const subscriptionType = SubscriptionType.BASIC; - const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType); + const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType, requestDetails); expect(activePlans).to.deep.equal([]); }); it('returns all active plans for the subscription type', async () => { const subscriptionType = SubscriptionType.BASIC; - const createdPlan1 = await repository.create(subscriptionType); - const createdPlan2 = await repository.create(subscriptionType); + const createdPlan1 = await repository.create(subscriptionType, requestDetails); + const createdPlan2 = await repository.create(subscriptionType, requestDetails); - const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType); + const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType, requestDetails); expect(activePlans).to.have.lengthOf(2); expect(activePlans.map((plan) => plan.id)).to.include.members([createdPlan1.id, createdPlan2.id]); }); it('does not return inactive plans for the subscription type', async () => { const subscriptionType = SubscriptionType.BASIC; - const activePlan = await repository.create(subscriptionType); - const inactivePlan = await repository.create(subscriptionType); + const activePlan = await repository.create(subscriptionType, requestDetails); + const inactivePlan = await repository.create(subscriptionType, requestDetails); // Manually set the plan to inactive const key = `${repository['collectionKey']}:${inactivePlan.id}`; - await cacheService.set(key, { ...inactivePlan, active: false }, 'test'); + await cacheService.set(key, { ...inactivePlan, active: false }, 'test', requestDetails); - const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType); + const activePlans = await repository.findAllActiveBySubscriptionType(subscriptionType, requestDetails); expect(activePlans).to.deep.equal([activePlan]); }); it('returns only active plans for the specified subscription type', async () => { - const basicPlan = await repository.create(SubscriptionType.BASIC); - const extendedPlan = await repository.create(SubscriptionType.EXTENDED); - const privilegedPlan = await repository.create(SubscriptionType.PRIVILEGED); + const basicPlan = await repository.create(SubscriptionType.BASIC, requestDetails); + const extendedPlan = await repository.create(SubscriptionType.EXTENDED, requestDetails); + const privilegedPlan = await repository.create(SubscriptionType.PRIVILEGED, requestDetails); - const activeBasicPlans = await repository.findAllActiveBySubscriptionType(SubscriptionType.BASIC); + const activeBasicPlans = await repository.findAllActiveBySubscriptionType( + SubscriptionType.BASIC, + requestDetails, + ); expect(activeBasicPlans).to.have.lengthOf(1); expect(activeBasicPlans[0].id).to.equal(basicPlan.id); - const activeExtendedPlans = await repository.findAllActiveBySubscriptionType(SubscriptionType.EXTENDED); + const activeExtendedPlans = await repository.findAllActiveBySubscriptionType( + SubscriptionType.EXTENDED, + requestDetails, + ); expect(activeExtendedPlans).to.have.lengthOf(1); expect(activeExtendedPlans[0].id).to.equal(extendedPlan.id); - const activePrivilegedPlans = await repository.findAllActiveBySubscriptionType(SubscriptionType.PRIVILEGED); + const activePrivilegedPlans = await repository.findAllActiveBySubscriptionType( + SubscriptionType.PRIVILEGED, + requestDetails, + ); expect(activePrivilegedPlans).to.have.lengthOf(1); expect(activePrivilegedPlans[0].id).to.equal(privilegedPlan.id); }); diff --git a/packages/relay/tests/lib/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.spec.ts b/packages/relay/tests/lib/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.spec.ts index 2debf7cee8..d3a63da701 100644 --- a/packages/relay/tests/lib/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.spec.ts +++ b/packages/relay/tests/lib/repositories/hbarLimiter/ipAddressHbarSpendingPlanRepository.spec.ts @@ -28,12 +28,14 @@ import { IPAddressHbarSpendingPlanNotFoundError } from '../../../../src/lib/db/t import { randomBytes, uuidV4 } from 'ethers'; import { Registry } from 'prom-client'; import { useInMemoryRedisServer } from '../../../helpers'; +import { RequestDetails } from '../../../../src/lib/types'; chai.use(chaiAsPromised); describe('IPAddressHbarSpendingPlanRepository', function () { const logger = pino(); const registry = new Registry(); + const requestDetails = new RequestDetails({ requestId: 'testId', ipAddress: '0.0.0.0' }); const tests = (isSharedCacheEnabled: boolean) => { let cacheService: CacheService; @@ -60,14 +62,14 @@ describe('IPAddressHbarSpendingPlanRepository', function () { describe('findByAddress', () => { it('retrieves an address plan by ip', async () => { const addressPlan: IIPAddressHbarSpendingPlan = { ipAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test', requestDetails); - const result = await repository.findByAddress(ipAddress); + const result = await repository.findByAddress(ipAddress, requestDetails); expect(result).to.deep.equal(addressPlan); }); it('throws an error if address plan is not found', async () => { - await expect(repository.findByAddress(nonExistingIpAddress)).to.be.eventually.rejectedWith( + await expect(repository.findByAddress(nonExistingIpAddress, requestDetails)).to.be.eventually.rejectedWith( IPAddressHbarSpendingPlanNotFoundError, `IPAddressHbarSpendingPlan with address ${nonExistingIpAddress} not found`, ); @@ -78,24 +80,26 @@ describe('IPAddressHbarSpendingPlanRepository', function () { it('saves an address plan successfully', async () => { const addressPlan: IIPAddressHbarSpendingPlan = { ipAddress, planId: uuidV4(randomBytes(16)) }; - await repository.save(addressPlan); + await repository.save(addressPlan, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ipAddress}`, 'test', + requestDetails, ); expect(result).to.deep.equal(addressPlan); }); it('overwrites an existing address plan', async () => { const addressPlan: IIPAddressHbarSpendingPlan = { ipAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test', requestDetails); const newPlanId = uuidV4(randomBytes(16)); const newAddressPlan: IIPAddressHbarSpendingPlan = { ipAddress, planId: newPlanId }; - await repository.save(newAddressPlan); + await repository.save(newAddressPlan, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ipAddress}`, 'test', + requestDetails, ); expect(result).to.deep.equal(newAddressPlan); }); @@ -104,18 +108,19 @@ describe('IPAddressHbarSpendingPlanRepository', function () { describe('delete', () => { it('deletes an address plan successfully', async () => { const addressPlan: IIPAddressHbarSpendingPlan = { ipAddress, planId: uuidV4(randomBytes(16)) }; - await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test'); + await cacheService.set(`${repository['collectionKey']}:${ipAddress}`, addressPlan, 'test', requestDetails); - await repository.delete(ipAddress); + await repository.delete(ipAddress, requestDetails); const result = await cacheService.getAsync( `${repository['collectionKey']}:${ipAddress}`, 'test', + requestDetails, ); expect(result).to.be.null; }); it('does not throw an error if address plan to delete does not exist', async () => { - await expect(repository.delete(nonExistingIpAddress)).to.be.fulfilled; + await expect(repository.delete(nonExistingIpAddress, requestDetails)).to.be.fulfilled; }); }); }; diff --git a/packages/relay/tests/lib/sdkClient.spec.ts b/packages/relay/tests/lib/sdkClient.spec.ts index 5fb374b0d3..5aac26123b 100644 --- a/packages/relay/tests/lib/sdkClient.spec.ts +++ b/packages/relay/tests/lib/sdkClient.spec.ts @@ -25,7 +25,6 @@ import { resolve } from 'path'; import * as sinon from 'sinon'; import { config } from 'dotenv'; import { Context } from 'mocha'; -import { v4 as uuid } from 'uuid'; import EventEmitter from 'events'; import { Registry } from 'prom-client'; import { Utils } from '../../src/utils'; @@ -59,6 +58,7 @@ import { FileDeleteTransaction, TransactionRecordQuery, } from '@hashgraph/sdk'; +import { RequestDetails } from '../../src/lib/types'; config({ path: resolve(__dirname, '../test.env') }); const registry = new Registry(); @@ -76,6 +76,7 @@ describe('SdkClient', async function () { let metricService: MetricService; let mirrorNodeClient: MirrorNodeClient; + const requestDetails = new RequestDetails({ requestId: 'testId', ipAddress: '0.0.0.0' }); const feeSchedules = { current: { transactionFeeSchedule: [ @@ -159,7 +160,14 @@ describe('SdkClient', async function () { it('executes the query', async () => { queryStub.returns(successResponse); - let { resp, cost } = await sdkClient.increaseCostAndRetryExecution(contractCallQuery, baseCost, client, 3, 0); + let { resp, cost } = await sdkClient.increaseCostAndRetryExecution( + contractCallQuery, + baseCost, + client, + 3, + 0, + requestDetails, + ); expect(resp).to.eq(successResponse); expect(cost.toTinybars().toNumber()).to.eq(costTinybars); expect(queryStub.callCount).to.eq(1); @@ -171,7 +179,14 @@ describe('SdkClient', async function () { }); queryStub.onCall(1).returns(successResponse); - let { resp, cost } = await sdkClient.increaseCostAndRetryExecution(contractCallQuery, baseCost, client, 3, 0); + let { resp, cost } = await sdkClient.increaseCostAndRetryExecution( + contractCallQuery, + baseCost, + client, + 3, + 0, + requestDetails, + ); expect(resp).to.eq(successResponse); expect(cost.toTinybars().toNumber()).to.eq(costTinybars * constants.QUERY_COST_INCREMENTATION_STEP); expect(queryStub.callCount).to.eq(2); @@ -188,7 +203,14 @@ describe('SdkClient', async function () { queryStub.onCall(2).returns(successResponse); - let { resp, cost } = await sdkClient.increaseCostAndRetryExecution(contractCallQuery, baseCost, client, 3, 0); + let { resp, cost } = await sdkClient.increaseCostAndRetryExecution( + contractCallQuery, + baseCost, + client, + 3, + 0, + requestDetails, + ); expect(resp).to.eq(successResponse); expect(cost.toTinybars().toNumber()).to.eq( Math.floor(costTinybars * Math.pow(constants.QUERY_COST_INCREMENTATION_STEP, 2)), @@ -202,7 +224,7 @@ describe('SdkClient', async function () { status: Status.InsufficientTxFee, }); - await sdkClient.increaseCostAndRetryExecution(contractCallQuery, baseCost, client, 3, 0); + await sdkClient.increaseCostAndRetryExecution(contractCallQuery, baseCost, client, 3, 0, requestDetails); } catch (e: any) { expect(queryStub.callCount).to.eq(4); expect(e.status).to.eq(Status.InsufficientTxFee); @@ -219,7 +241,7 @@ describe('SdkClient', async function () { const convertGasPriceToTinyBarsStub = sinon.stub(sdkClient, 'convertGasPriceToTinyBars').callsFake(() => 0x160c); for (let i = 0; i < 5; i++) { - await sdkClient.getTinyBarGasFee(''); + await sdkClient.getTinyBarGasFee('', requestDetails); } sinon.assert.calledOnce(getFeeScheduleStub); @@ -2243,12 +2265,10 @@ describe('SdkClient', async function () { const mockedConstructorName = 'constructor_name'; const mockedInteractingEntity = 'interacting_entity'; - let requestId: string; let hbarLimitMock: sinon.SinonMock; let sdkClientMock: sinon.SinonMock; beforeEach(() => { - requestId = uuid(); hbarLimitMock = sinon.mock(hbarLimiter); sdkClientMock = sinon.mock(sdkClient); mock = new MockAdapter(instance); @@ -2279,10 +2299,10 @@ describe('SdkClient', async function () { await sdkClient.submitEthereumTransaction( transactionBuffer, mockedCallerName, + requestDetails, randomAccountAddress, mockedNetworkGasPrice, mockedExchangeRateIncents, - requestId, ); expect.fail(`Expected an error but nothing was thrown`); } catch (error: any) { @@ -2338,10 +2358,10 @@ describe('SdkClient', async function () { await sdkClient.submitEthereumTransaction( transactionBuffer, mockedCallerName, + requestDetails, randomAccountAddress, mockedNetworkGasPrice, mockedExchangeRateIncents, - requestId, ); expect(queryStub.called).to.be.true; @@ -2384,7 +2404,7 @@ describe('SdkClient', async function () { const response = await sdkClient.createFile( callData, client, - requestId, + requestDetails, mockedCallerName, mockedInteractingEntity, randomAccountAddress, @@ -2421,7 +2441,7 @@ describe('SdkClient', async function () { new FileAppendTransaction(), mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, true, randomAccountAddress, ); @@ -2447,7 +2467,7 @@ describe('SdkClient', async function () { new FileAppendTransaction(), mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, true, randomAccountAddress, ); @@ -2467,8 +2487,9 @@ describe('SdkClient', async function () { await sdkClient.submitEthereumTransaction( transactionBuffer, mockedCallerName, - requestId, + requestDetails, randomAccountAddress, + mockedNetworkGasPrice, mockedExchangeRateIncents, ); expect.fail(`Expected an error but nothing was thrown`); @@ -2500,7 +2521,7 @@ describe('SdkClient', async function () { const response = await sdkClient.createFile( callData, client, - requestId, + requestDetails, mockedCallerName, mockedInteractingEntity, randomAccountAddress, @@ -2525,7 +2546,13 @@ describe('SdkClient', async function () { hbarLimitMock.expects('addExpense').withArgs(mockedTransactionRecordFee).once(); hbarLimitMock.expects('shouldLimit').never(); - await sdkClient.deleteFile(fileId, requestId, mockedCallerName, mockedInteractingEntity, randomAccountAddress); + await sdkClient.deleteFile( + fileId, + requestDetails, + mockedCallerName, + mockedInteractingEntity, + randomAccountAddress, + ); expect(deleteFileStub.called).to.be.true; expect(fileInfoQueryStub.called).to.be.true; @@ -2543,7 +2570,7 @@ describe('SdkClient', async function () { client, mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, ); expect(result).to.equal(fileInfo); @@ -2562,7 +2589,7 @@ describe('SdkClient', async function () { client, mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, ); expect(result).to.equal(fileInfo); @@ -2590,7 +2617,7 @@ describe('SdkClient', async function () { new EthereumTransaction().setCallDataFileId(fileId).setEthereumData(transactionBuffer), mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, true, randomAccountAddress, ); @@ -2640,7 +2667,7 @@ describe('SdkClient', async function () { new EthereumTransaction().setCallDataFileId(fileId).setEthereumData(transactionBuffer), mockedCallerName, mockedInteractingEntity, - requestId, + requestDetails, true, randomAccountAddress, ); @@ -2672,9 +2699,9 @@ describe('SdkClient', async function () { const transactionRecordMetrics = await sdkClient.getTransactionRecordMetrics( transactionId.toString(), mockedCallerName, - requestId, mockedConstructorName, accountId.toString(), + requestDetails, ); expect(transactionRecordStub.called).to.be.true; @@ -2691,12 +2718,12 @@ describe('SdkClient', async function () { await sdkClient.getTransactionRecordMetrics( transactionId.toString(), mockedCallerName, - requestId, mockedConstructorName, accountId.toString(), + requestDetails, ); expect.fail('should have thrown an error'); - } catch (error) { + } catch (error: any) { expect(error.status).to.eq(expectedError.status); expect(error.message).to.eq(expectedError.message); } diff --git a/packages/relay/tests/lib/services/cacheService/cacheService.spec.ts b/packages/relay/tests/lib/services/cacheService/cacheService.spec.ts index 95ad7b6deb..931e537f0a 100644 --- a/packages/relay/tests/lib/services/cacheService/cacheService.spec.ts +++ b/packages/relay/tests/lib/services/cacheService/cacheService.spec.ts @@ -27,6 +27,7 @@ import chai, { expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; import * as sinon from 'sinon'; import { useInMemoryRedisServer } from '../../../helpers'; +import { RequestDetails } from '../../../../dist/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); const logger = pino(); @@ -39,7 +40,7 @@ chai.use(chaiAsPromised); describe('CacheService Test Suite', async function () { this.timeout(10000); - + const requestDetails = new RequestDetails({ requestId: 'cacheServiceTest', ipAddress: '0.0.0.0' }); const describeKeysTestSuite = () => { describe('keys', async function () { it('should retrieve all keys', async function () { @@ -48,9 +49,9 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); - const keys = await cacheService.keys('*', callingMethod); + const keys = await cacheService.keys('*', callingMethod, requestDetails); expect(keys).to.have.members(Object.keys(entries)); }); @@ -60,9 +61,9 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); - const keys = await cacheService.keys('key*', callingMethod); + const keys = await cacheService.keys('key*', callingMethod, requestDetails); expect(keys).to.have.members(Object.keys(entries)); }); @@ -72,9 +73,9 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); - const keys = await cacheService.keys('key?', callingMethod); + const keys = await cacheService.keys('key?', callingMethod, requestDetails); expect(keys).to.have.members(Object.keys(entries)); }); @@ -84,9 +85,9 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); - const keys = await cacheService.keys('key[1-2]', callingMethod); + const keys = await cacheService.keys('key[1-2]', callingMethod, requestDetails); expect(keys).to.have.members(['key1', 'key2']); }); @@ -96,10 +97,10 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); // [^3] should match all keys except key3 - const keys = await cacheService.keys('key[^3]', callingMethod); + const keys = await cacheService.keys('key[^3]', callingMethod, requestDetails); expect(keys).to.have.members(['key1', 'key2']); }); @@ -109,9 +110,9 @@ describe('CacheService Test Suite', async function () { entries['keyb'] = 'value2'; entries['keyc'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); - const keys = await cacheService.keys('key[a-c]', callingMethod); + const keys = await cacheService.keys('key[a-c]', callingMethod, requestDetails); expect(keys).to.have.members(Object.keys(entries)); }); @@ -119,9 +120,9 @@ describe('CacheService Test Suite', async function () { const key = 'h*llo'; const value = 'value'; - await cacheService.set(key, value, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); - const keys = await cacheService.keys('h*llo', callingMethod); + const keys = await cacheService.keys('h*llo', callingMethod, requestDetails); expect(keys).to.have.members([key]); }); @@ -132,8 +133,8 @@ describe('CacheService Test Suite', async function () { entries['key3'] = 'value3'; await cacheService.disconnectRedisClient(); - await cacheService.multiSet(entries, callingMethod); - const keys = await cacheService.keys('*', callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); + const keys = await cacheService.keys('*', callingMethod, requestDetails); expect(keys).to.have.members(Object.keys(entries)); }); }); @@ -146,15 +147,15 @@ describe('CacheService Test Suite', async function () { }); this.afterEach(async () => { - await cacheService.clear(); + await cacheService.clear(requestDetails); }); it('should be able to set and get from internal cache', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); - const cachedValue = await cacheService.getAsync(key, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).eq(value); }); @@ -163,9 +164,9 @@ describe('CacheService Test Suite', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); - await cacheService.delete(key, callingMethod); - const cachedValue = await cacheService.getAsync(key, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); + await cacheService.delete(key, callingMethod, requestDetails); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); @@ -174,8 +175,8 @@ describe('CacheService Test Suite', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); - const cachedValue = await cacheService.getAsync(key, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).eq(value); }); @@ -186,10 +187,10 @@ describe('CacheService Test Suite', async function () { entries['key2'] = 'value2'; entries['key3'] = 'value3'; - await cacheService.multiSet(entries, callingMethod); + await cacheService.multiSet(entries, callingMethod, requestDetails); for (const [key, value] of Object.entries(entries)) { - const valueFromCache = await cacheService.getAsync(key, callingMethod); + const valueFromCache = await cacheService.getAsync(key, callingMethod, requestDetails); expect(valueFromCache).eq(value); } }); @@ -199,8 +200,8 @@ describe('CacheService Test Suite', async function () { const key = 'counter'; const amount = 5; - await cacheService.set(key, 10, callingMethod); - const newValue = await cacheService.incrBy(key, amount, callingMethod); + await cacheService.set(key, 10, callingMethod, requestDetails); + const newValue = await cacheService.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).to.equal(15); }); @@ -211,8 +212,8 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const value = 'item'; - await cacheService.rPush(key, value, callingMethod); - const cachedValue = await cacheService.getAsync(key, callingMethod); + await cacheService.rPush(key, value, callingMethod, requestDetails); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).to.deep.equal([value]); }); @@ -223,8 +224,8 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const values = ['item1', 'item2', 'item3']; - await cacheService.set(key, values, callingMethod); - const range = await cacheService.lRange(key, 0, 1, callingMethod); + await cacheService.set(key, values, callingMethod, requestDetails); + const range = await cacheService.lRange(key, 0, 1, callingMethod, requestDetails); expect(range).to.deep.equal(['item1', 'item2']); }); @@ -233,8 +234,8 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const values = ['item1', 'item2', 'item3']; - await cacheService.set(key, values, callingMethod); - const range = await cacheService.lRange(key, -2, -1, callingMethod); + await cacheService.set(key, values, callingMethod, requestDetails); + const range = await cacheService.lRange(key, -2, -1, callingMethod, requestDetails); expect(range).to.deep.equal(['item2', 'item3']); }); @@ -294,16 +295,16 @@ describe('CacheService Test Suite', async function () { }); this.afterEach(async () => { - await cacheService.clear(); + await cacheService.clear(requestDetails); }); it('should be able to set and get from shared cache', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); - const cachedValue = await cacheService.getAsync(key, callingMethod); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).eq(value); }); @@ -311,11 +312,11 @@ describe('CacheService Test Suite', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); - await cacheService.delete(key, callingMethod); + await cacheService.delete(key, callingMethod, requestDetails); - const cachedValue = await cacheService.getAsync(key, callingMethod); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).to.be.null; }); @@ -323,17 +324,17 @@ describe('CacheService Test Suite', async function () { const key = 'string'; const value = 'value'; - await cacheService.set(key, value, callingMethod); + await cacheService.set(key, value, callingMethod, requestDetails); - const cachedValue = await cacheService.getAsync(key, callingMethod); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).eq(value); }); it('should be able to set using multiSet and get them separately using internal cache', async function () { - await cacheService.multiSet(multiSetEntries, callingMethod); + await cacheService.multiSet(multiSetEntries, callingMethod, requestDetails); for (const [key, value] of Object.entries(multiSetEntries)) { - const valueFromCache = await cacheService.getAsync(key, callingMethod); + const valueFromCache = await cacheService.getAsync(key, callingMethod, requestDetails); expect(valueFromCache).eq(value); } }); @@ -342,10 +343,10 @@ describe('CacheService Test Suite', async function () { // @ts-ignore cacheService['shouldMultiSet'] = false; - await cacheService.multiSet(multiSetEntries, callingMethod); + await cacheService.multiSet(multiSetEntries, callingMethod, requestDetails); for (const [key, value] of Object.entries(multiSetEntries)) { - const valueFromCache = await cacheService.getAsync(key, callingMethod); + const valueFromCache = await cacheService.getAsync(key, callingMethod, requestDetails); expect(valueFromCache).eq(value); } }); @@ -354,7 +355,7 @@ describe('CacheService Test Suite', async function () { const key = 'string'; await cacheService.disconnectRedisClient(); - const cachedValue = await cacheService.getAsync(key, callingMethod); + const cachedValue = await cacheService.getAsync(key, callingMethod, requestDetails); expect(cachedValue).eq(null); }); @@ -364,19 +365,19 @@ describe('CacheService Test Suite', async function () { await cacheService.disconnectRedisClient(); - await expect(cacheService.set(key, value, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.set(key, value, callingMethod, requestDetails)).to.eventually.not.be.rejected; - const internalCacheRes = await cacheService.getAsync(key, callingMethod); + const internalCacheRes = await cacheService.getAsync(key, callingMethod, requestDetails); expect(internalCacheRes).to.eq(value); }); it('should be able to multiSet to internal cache in case of Redis error', async function () { await cacheService.disconnectRedisClient(); - await expect(cacheService.multiSet(multiSetEntries, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.multiSet(multiSetEntries, callingMethod, requestDetails)).to.eventually.not.be.rejected; for (const [key, value] of Object.entries(multiSetEntries)) { - const internalCacheRes = await cacheService.getAsync(key, callingMethod); + const internalCacheRes = await cacheService.getAsync(key, callingMethod, requestDetails); expect(internalCacheRes).to.eq(value); } }); @@ -387,10 +388,10 @@ describe('CacheService Test Suite', async function () { await cacheService.disconnectRedisClient(); - await expect(cacheService.multiSet(multiSetEntries, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.multiSet(multiSetEntries, callingMethod, requestDetails)).to.eventually.not.be.rejected; for (const [key, value] of Object.entries(multiSetEntries)) { - const internalCacheRes = await cacheService.getAsync(key, callingMethod); + const internalCacheRes = await cacheService.getAsync(key, callingMethod, requestDetails); expect(internalCacheRes).to.eq(value); } }); @@ -398,21 +399,21 @@ describe('CacheService Test Suite', async function () { it('should be able to clear from internal cache in case of Redis error', async function () { await cacheService.disconnectRedisClient(); - await expect(cacheService.clear()).to.eventually.not.be.rejected; + await expect(cacheService.clear(requestDetails)).to.eventually.not.be.rejected; }); it('should be able to delete from internal cache in case of Redis error', async function () { const key = 'string'; await cacheService.disconnectRedisClient(); - await expect(cacheService.delete(key, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.delete(key, callingMethod, requestDetails)).to.eventually.not.be.rejected; }); it('should be able to set to shared cache', async function () { const key = 'string'; const value = 'value'; - await expect(cacheService.set(key, value, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.set(key, value, callingMethod, requestDetails)).to.eventually.not.be.rejected; }); it('should be able to multiset to shared cache', async function () { @@ -420,13 +421,13 @@ describe('CacheService Test Suite', async function () { items['key1'] = 'value1'; items['key2'] = 'value2'; - await expect(cacheService.multiSet(items, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.multiSet(items, callingMethod, requestDetails)).to.eventually.not.be.rejected; }); it('should be able to delete from shared cache', async function () { const key = 'string'; - await expect(cacheService.delete(key, callingMethod)).to.eventually.not.be.rejected; + await expect(cacheService.delete(key, callingMethod, requestDetails)).to.eventually.not.be.rejected; }); describe('incrBy', async function () { @@ -434,8 +435,8 @@ describe('CacheService Test Suite', async function () { const key = 'counter'; const amount = 5; - await cacheService.set(key, 10, callingMethod); - const newValue = await cacheService.incrBy(key, amount, callingMethod); + await cacheService.set(key, 10, callingMethod, requestDetails); + const newValue = await cacheService.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).to.equal(15); }); @@ -444,8 +445,8 @@ describe('CacheService Test Suite', async function () { const key = 'counter'; const amount = 5; - await cacheService.set(key, 10, callingMethod); - const newValue = await cacheService.incrBy(key, amount, callingMethod); + await cacheService.set(key, 10, callingMethod, requestDetails); + const newValue = await cacheService.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).to.equal(15); }); @@ -456,7 +457,7 @@ describe('CacheService Test Suite', async function () { await cacheService.disconnectRedisClient(); - const newValue = await cacheService.incrBy(key, amount, callingMethod); + const newValue = await cacheService.incrBy(key, amount, callingMethod, requestDetails); expect(newValue).to.equal(5); }); @@ -467,8 +468,8 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const value = 'item'; - await cacheService.rPush(key, value, callingMethod); - const cachedValue = await cacheService.lRange(key, 0, -1, callingMethod); + await cacheService.rPush(key, value, callingMethod, requestDetails); + const cachedValue = await cacheService.lRange(key, 0, -1, callingMethod, requestDetails); expect(cachedValue).to.deep.equal([value]); }); @@ -479,8 +480,8 @@ describe('CacheService Test Suite', async function () { await cacheService.disconnectRedisClient(); - await cacheService.rPush(key, value, callingMethod); - const cachedValue = await cacheService.lRange(key, 0, -1, callingMethod); + await cacheService.rPush(key, value, callingMethod, requestDetails); + const cachedValue = await cacheService.lRange(key, 0, -1, callingMethod, requestDetails); expect(cachedValue).to.deep.equal([value]); }); @@ -491,10 +492,10 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const values = ['item1', 'item2', 'item3']; for (const item of values) { - await cacheService.rPush(key, item, callingMethod); + await cacheService.rPush(key, item, callingMethod, requestDetails); } - const range = await cacheService.lRange(key, 0, 1, callingMethod); + const range = await cacheService.lRange(key, 0, 1, callingMethod, requestDetails); expect(range).to.deep.equal(['item1', 'item2']); }); @@ -503,10 +504,10 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const values = ['item1', 'item2', 'item3']; for (const item of values) { - await cacheService.rPush(key, item, callingMethod); + await cacheService.rPush(key, item, callingMethod, requestDetails); } - const range = await cacheService.lRange(key, -2, -1, callingMethod); + const range = await cacheService.lRange(key, -2, -1, callingMethod, requestDetails); expect(range).to.deep.equal(['item2', 'item3']); }); @@ -517,10 +518,10 @@ describe('CacheService Test Suite', async function () { const key = 'list'; const values = ['item1', 'item2', 'item3']; for (const item of values) { - await cacheService.rPush(key, item, callingMethod); + await cacheService.rPush(key, item, callingMethod, requestDetails); } - const range = await cacheService.lRange(key, 0, 1, callingMethod); + const range = await cacheService.lRange(key, 0, 1, callingMethod, requestDetails); expect(range).to.deep.equal(['item1', 'item2']); }); diff --git a/packages/relay/tests/lib/services/debugService/debug.spec.ts b/packages/relay/tests/lib/services/debugService/debug.spec.ts index 0f7eb97523..41e7e36294 100644 --- a/packages/relay/tests/lib/services/debugService/debug.spec.ts +++ b/packages/relay/tests/lib/services/debugService/debug.spec.ts @@ -28,13 +28,14 @@ import { MirrorNodeClient } from '../../../../src/lib/clients'; import pino from 'pino'; import { TracerType } from '../../../../src/lib/constants'; import { DebugService } from '../../../../src/lib/services/debugService'; -import { getQueryParams, getRequestId } from '../../../helpers'; +import { getQueryParams } from '../../../helpers'; import RelayAssertions from '../../../assertions'; import { predefined } from '../../../../src'; import { CacheService } from '../../../../src/lib/services/cacheService/cacheService'; import { CommonService } from '../../../../src/lib/services/ethService'; import { IOpcodesResponse } from '../../../../src/lib/clients/models/IOpcodesResponse'; import { strip0x } from '../../../../src/formatters'; +import { RequestDetails } from '../../../../src/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); chai.use(chaiAsPromised); @@ -51,6 +52,7 @@ let cacheService: CacheService; describe('Debug API Test Suite', async function () { this.timeout(10000); + const requestDetails = new RequestDetails({ requestId: 'debugTest', ipAddress: '0.0.0.0' }); const transactionHash = '0xb7a433b014684558d4154c73de3ed360bd5867725239938c2143acb7a76bca82'; const nonExistentTransactionHash = '0xb8a433b014684558d4154c73de3ed360bd5867725239938c2143acb7a76bca82'; const contractAddress = '0x0000000000000000000000000000000000000409'; @@ -337,7 +339,7 @@ describe('Debug API Test Suite', async function () { debugService.debug_traceTransaction, true, debugService, - [transactionHash, callTracer, tracerConfigFalse, getRequestId()], + [transactionHash, callTracer, tracerConfigFalse, requestDetails], ); }); @@ -348,7 +350,7 @@ describe('Debug API Test Suite', async function () { transactionHash, callTracer, tracerConfigFalse, - getRequestId(), + requestDetails, ); expect(traceTransaction).to.exist; }); @@ -360,7 +362,7 @@ describe('Debug API Test Suite', async function () { debugService.debug_traceTransaction, true, debugService, - [transactionHash, callTracer, tracerConfigFalse, getRequestId()], + [transactionHash, callTracer, tracerConfigFalse, requestDetails], ); }); }); @@ -398,7 +400,7 @@ describe('Debug API Test Suite', async function () { transactionHash, callTracer, tracerConfigFalse, - getRequestId(), + requestDetails, ); expect(result).to.deep.equal(expectedResult); @@ -420,7 +422,7 @@ describe('Debug API Test Suite', async function () { transactionHash, callTracer, tracerConfigTrue, - getRequestId(), + requestDetails, ); expect(result).to.deep.equal(expectedResult); @@ -467,7 +469,7 @@ describe('Debug API Test Suite', async function () { transactionHash, opcodeLogger, config, - getRequestId(), + requestDetails, ); expect(result).to.deep.equal(expectedResult); @@ -509,7 +511,7 @@ describe('Debug API Test Suite', async function () { nonExistentTransactionHash, callTracer, tracerConfigTrue, - getRequestId(), + requestDetails, ]); }); @@ -527,13 +529,13 @@ describe('Debug API Test Suite', async function () { describe('resolveAddress', async function () { it('should return null address with invalid parameters in resolveAddress', async function () { - const address = await debugService.resolveAddress(null!); + const address = await debugService.resolveAddress(null!, requestDetails); expect(address).to.be.null; }); it('should return passed address on notFound entity from the mirror node', async function () { restMock.onGet(ACCOUNT_BY_ADDRESS).reply(404, notFound); - const address = await debugService.resolveAddress(accountAddress); + const address = await debugService.resolveAddress(accountAddress, requestDetails); expect(address).to.eq(accountAddress); }); }); diff --git a/packages/relay/tests/lib/services/eth/filter.spec.ts b/packages/relay/tests/lib/services/eth/filter.spec.ts index 3196cf9041..bf09d6658a 100644 --- a/packages/relay/tests/lib/services/eth/filter.spec.ts +++ b/packages/relay/tests/lib/services/eth/filter.spec.ts @@ -23,15 +23,17 @@ import dotenv from 'dotenv'; import MockAdapter from 'axios-mock-adapter'; import { expect } from 'chai'; import { Registry } from 'prom-client'; -import { MirrorNodeClient } from '../../../../src/lib/clients/mirrorNodeClient'; +import { MirrorNodeClient } from '../../../../src/lib/clients'; import pino from 'pino'; import constants from '../../../../src/lib/constants'; import { FilterService, CommonService } from '../../../../src/lib/services/ethService'; -import { defaultEvmAddress, getRequestId, toHex, defaultBlock, defaultLogTopics, defaultLogs1 } from '../../../helpers'; +import { defaultEvmAddress, toHex, defaultBlock, defaultLogTopics, defaultLogs1 } from '../../../helpers'; import RelayAssertions from '../../../assertions'; import { predefined } from '../../../../src'; import { CacheService } from '../../../../src/lib/services/cacheService/cacheService'; import * as sinon from 'sinon'; +import { RequestDetails } from '../../../../src/lib/types'; +import { v4 as uuid } from 'uuid'; dotenv.config({ path: path.resolve(__dirname, '../test.env') }); const logger = pino(); @@ -45,6 +47,7 @@ let cacheService: CacheService; describe('Filter API Test Suite', async function () { this.timeout(10000); + const requestDetails = new RequestDetails({ requestId: uuid(), ipAddress: '0.0.0.0' }); const filterObject = { toBlock: 'latest', }; @@ -59,7 +62,7 @@ describe('Filter API Test Suite', async function () { const validateFilterCache = async (filterId, expectedFilterType, expectedParams = {}) => { const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; - const cachedFilter = await cacheService.getAsync(cacheKey, undefined); + const cachedFilter = await cacheService.getAsync(cacheKey, undefined, requestDetails); expect(cachedFilter).to.exist; expect(cachedFilter.type).to.exist; expect(cachedFilter.type).to.eq(expectedFilterType); @@ -90,7 +93,7 @@ describe('Filter API Test Suite', async function () { cacheService = new CacheService(logger.child({ name: `cache` }), registry); // @ts-ignore mirrorNodeInstance = new MirrorNodeClient( - process.env.MIRROR_NODE_URL, + process.env.MIRROR_NODE_URL ?? '', logger.child({ name: `mirror-node` }), registry, cacheService, @@ -109,7 +112,7 @@ describe('Filter API Test Suite', async function () { this.beforeEach(() => { // reset cache and restMock - cacheService.clear(); + cacheService.clear(requestDetails); restMock.reset(); cacheMock.stub(cacheService, 'set').returns(true); @@ -139,28 +142,28 @@ describe('Filter API Test Suite', async function () { filterService.newFilter, true, filterService, - {}, + [undefined, undefined, requestDetails], ); await RelayAssertions.assertRejection( predefined.UNSUPPORTED_METHOD, filterService.uninstallFilter, true, filterService, - [existingFilterId], + [existingFilterId, requestDetails], ); await RelayAssertions.assertRejection( predefined.UNSUPPORTED_METHOD, filterService.getFilterChanges, true, filterService, - [existingFilterId], + [existingFilterId, requestDetails], ); }); it('FILTER_API_ENABLED=true', async function () { process.env.FILTER_API_ENABLED = 'true'; restMock.onGet(LATEST_BLOCK_QUERY).reply(200, { blocks: [{ ...defaultBlock }] }); - const filterId = await filterService.newFilter(); + const filterId = await filterService.newFilter(undefined, undefined, requestDetails); expect(filterId).to.exist; expect(RelayAssertions.validateHash(filterId, 32)).to.eq(true, 'returns valid filterId'); @@ -173,13 +176,13 @@ describe('Filter API Test Suite', async function () { `contracts/results/logs?timestamp=gte:${defaultBlock.timestamp.from}×tamp=lte:${defaultBlock.timestamp.to}&limit=100&order=asc`, ) .reply(200, defaultLogs1); - const filterChanges = await filterService.getFilterChanges(filterId); + const filterChanges = await filterService.getFilterChanges(filterId, requestDetails); expect(filterChanges).to.exist; cacheMock.restore(); cacheMock.stub(cacheService, 'getAsync').withArgs(cacheKey, 'eth_uninstallFilter').returns(logFilterObject); - const isFilterUninstalled = await filterService.uninstallFilter(filterId); + const isFilterUninstalled = await filterService.uninstallFilter(filterId, requestDetails); expect(isFilterUninstalled).to.eq(true, 'executes correctly'); }); @@ -190,21 +193,21 @@ describe('Filter API Test Suite', async function () { filterService.newFilter, true, filterService, - [], + [undefined, undefined, requestDetails], ); await RelayAssertions.assertRejection( predefined.UNSUPPORTED_METHOD, filterService.uninstallFilter, true, filterService, - [existingFilterId], + [existingFilterId, requestDetails], ); await RelayAssertions.assertRejection( predefined.UNSUPPORTED_METHOD, filterService.getFilterChanges, true, filterService, - [existingFilterId], + [existingFilterId, requestDetails], ); }); }); @@ -231,27 +234,30 @@ describe('Filter API Test Suite', async function () { }); it('Returns a valid filterId', async function () { - expect(RelayAssertions.validateHash(await filterService.newFilter(), 32)).to.eq( - true, - 'with default param values', - ); - expect(RelayAssertions.validateHash(await filterService.newFilter(numberHex), 32)).to.eq(true, 'with fromBlock'); - expect(RelayAssertions.validateHash(await filterService.newFilter(numberHex, 'latest'), 32)).to.eq( - true, - 'with fromBlock, toBlock', - ); expect( - RelayAssertions.validateHash(await filterService.newFilter(numberHex, 'latest', defaultEvmAddress), 32), + RelayAssertions.validateHash(await filterService.newFilter(undefined, undefined, requestDetails), 32), + ).to.eq(true, 'with default param values'); + expect( + RelayAssertions.validateHash(await filterService.newFilter(numberHex, undefined, requestDetails), 32), + ).to.eq(true, 'with fromBlock'); + expect( + RelayAssertions.validateHash(await filterService.newFilter(numberHex, 'latest', requestDetails), 32), + ).to.eq(true, 'with fromBlock, toBlock'); + expect( + RelayAssertions.validateHash( + await filterService.newFilter(numberHex, 'latest', requestDetails, defaultEvmAddress), + 32, + ), ).to.eq(true, 'with fromBlock, toBlock, address'); expect( RelayAssertions.validateHash( - await filterService.newFilter(numberHex, 'latest', defaultEvmAddress, defaultLogTopics), + await filterService.newFilter(numberHex, 'latest', requestDetails, defaultEvmAddress, defaultLogTopics), 32, ), ).to.eq(true, 'with fromBlock, toBlock, address, topics'); expect( RelayAssertions.validateHash( - await filterService.newFilter(numberHex, 'latest', defaultEvmAddress, defaultLogTopics, getRequestId()), + await filterService.newFilter(numberHex, 'latest', requestDetails, defaultEvmAddress, defaultLogTopics), 32, ), ).to.eq(true, 'with all parameters'); @@ -261,13 +267,14 @@ describe('Filter API Test Suite', async function () { const filterId = await filterService.newFilter( numberHex, 'latest', + requestDetails, defaultEvmAddress, defaultLogTopics, - getRequestId(), ); - validateFilterCache(filterId, constants.FILTER.TYPE.LOG, { + await validateFilterCache(filterId, constants.FILTER.TYPE.LOG, { fromBlock: numberHex, toBlock: 'latest', + requestDetails: requestDetails, address: defaultEvmAddress, topics: defaultLogTopics, }); @@ -280,14 +287,14 @@ describe('Filter API Test Suite', async function () { filterService.newFilter, true, filterService, - [blockNumberHexes[1500], blockNumberHexes[1400]], + [blockNumberHexes[1500], blockNumberHexes[1400], requestDetails], ); await RelayAssertions.assertRejection( predefined.INVALID_BLOCK_RANGE, filterService.newFilter, true, filterService, - ['latest', blockNumberHexes[1400]], + ['latest', blockNumberHexes[1400], requestDetails], ); // block range is too large @@ -296,16 +303,22 @@ describe('Filter API Test Suite', async function () { filterService.newFilter, true, filterService, - [blockNumberHexes[5], blockNumberHexes[2000]], + [blockNumberHexes[5], blockNumberHexes[2000], requestDetails], ); // block range is valid expect( - RelayAssertions.validateHash(await filterService.newFilter(blockNumberHexes[1400], blockNumberHexes[1500]), 32), + RelayAssertions.validateHash( + await filterService.newFilter(blockNumberHexes[1400], blockNumberHexes[1500], requestDetails), + 32, + ), + ).to.eq(true); + expect( + RelayAssertions.validateHash( + await filterService.newFilter(blockNumberHexes[1400], 'latest', requestDetails), + 32, + ), ).to.eq(true); - expect(RelayAssertions.validateHash(await filterService.newFilter(blockNumberHexes[1400], 'latest'), 32)).to.eq( - true, - ); }); }); @@ -315,20 +328,24 @@ describe('Filter API Test Suite', async function () { cacheMock.stub(cacheService, 'getAsync').onFirstCall().returns(filterObject).onSecondCall().returns(undefined); - cacheService.set(cacheKey, filterObject, filterService.ethUninstallFilter, constants.FILTER.TTL, undefined, true); + await cacheService.set( + cacheKey, + filterObject, + filterService.ethUninstallFilter, + requestDetails, + constants.FILTER.TTL, + ); - const result = await filterService.uninstallFilter(existingFilterId); + const result = await filterService.uninstallFilter(existingFilterId, requestDetails); - const isDeleted = (await cacheService.getAsync(cacheKey, filterService.ethUninstallFilter, undefined)) - ? false - : true; + const isDeleted = !(await cacheService.getAsync(cacheKey, filterService.ethUninstallFilter, requestDetails)); expect(result).to.eq(true); expect(isDeleted).to.eq(true); }); it('should return false if filter does not exist, therefore is not deleted', async function () { cacheMock.stub(cacheService, 'getAsync').returns(undefined); - const result = await filterService.uninstallFilter(nonExistingFilterId); + const result = await filterService.uninstallFilter(nonExistingFilterId, requestDetails); expect(result).to.eq(false); }); }); @@ -339,12 +356,12 @@ describe('Filter API Test Suite', async function () { }); it('Returns a valid filterId', async function () { - expect(RelayAssertions.validateHash(await filterService.newBlockFilter(), 32)).to.eq(true); + expect(RelayAssertions.validateHash(await filterService.newBlockFilter(requestDetails), 32)).to.eq(true); }); it('Creates a filter with type=new_block', async function () { - const filterId = await filterService.newBlockFilter(getRequestId()); - validateFilterCache(filterId, constants.FILTER.TYPE.NEW_BLOCK, { + const filterId = await filterService.newBlockFilter(requestDetails); + await validateFilterCache(filterId, constants.FILTER.TYPE.NEW_BLOCK, { blockAtCreation: toHex(defaultBlock.number), }); }); @@ -353,13 +370,17 @@ describe('Filter API Test Suite', async function () { describe('eth_getFilterLogs', async function () { it('should throw FILTER_NOT_FOUND for type=newBlock', async function () { cacheMock.stub(cacheService, 'getAsync').returns(undefined); - const filterIdBlockType = await filterService.createFilter(constants.FILTER.TYPE.NEW_BLOCK, filterObject); + const filterIdBlockType = await filterService.createFilter( + constants.FILTER.TYPE.NEW_BLOCK, + filterObject, + requestDetails, + ); await RelayAssertions.assertRejection( predefined.FILTER_NOT_FOUND, filterService.getFilterLogs, true, filterService, - [filterIdBlockType], + [filterIdBlockType, requestDetails], ); }); @@ -368,13 +389,14 @@ describe('Filter API Test Suite', async function () { const filterIdBlockType = await filterService.createFilter( constants.FILTER.TYPE.PENDING_TRANSACTION, filterObject, + requestDetails, ); await RelayAssertions.assertRejection( predefined.FILTER_NOT_FOUND, filterService.getFilterLogs, true, filterService, - [filterIdBlockType], + [filterIdBlockType, requestDetails], ); }); @@ -400,7 +422,7 @@ describe('Filter API Test Suite', async function () { ) .reply(200, filteredLogs); - const filterId = await filterService.newFilter('0x1'); + const filterId = await filterService.newFilter('0x1', undefined, requestDetails); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock .stub(cacheService, 'getAsync') @@ -412,7 +434,7 @@ describe('Filter API Test Suite', async function () { }, }); - const logs = await filterService.getFilterLogs(filterId); + const logs = await filterService.getFilterLogs(filterId, requestDetails); expect(logs).to.not.be.empty; logs.every((log) => expect(Number(log.blockNumber)).to.be.greaterThan(1)); @@ -437,7 +459,7 @@ describe('Filter API Test Suite', async function () { ) .reply(200, filteredLogs); - const filterId = await filterService.newFilter(null, '0x3'); + const filterId = await filterService.newFilter(undefined, '0x3', requestDetails); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock .stub(cacheService, 'getAsync') @@ -450,7 +472,7 @@ describe('Filter API Test Suite', async function () { }, }); - const logs = await filterService.getFilterLogs(filterId); + const logs = await filterService.getFilterLogs(filterId, requestDetails); expect(logs).to.not.be.empty; logs.every((log) => expect(Number(log.blockNumber)).to.be.lessThan(3)); @@ -471,7 +493,7 @@ describe('Filter API Test Suite', async function () { ) .reply(200, filteredLogs); - const filterId = await filterService.newFilter(null, null, defaultEvmAddress); + const filterId = await filterService.newFilter(undefined, undefined, requestDetails, defaultEvmAddress); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock @@ -485,7 +507,7 @@ describe('Filter API Test Suite', async function () { }, }); - const logs = await filterService.getFilterLogs(filterId); + const logs = await filterService.getFilterLogs(filterId, requestDetails); expect(logs).to.not.be.empty; logs.every((log) => expect(log.address).to.equal(defaultEvmAddress)); @@ -511,7 +533,7 @@ describe('Filter API Test Suite', async function () { ) .reply(200, filteredLogs); - const filterId = await filterService.newFilter(null, null, null, customTopic); + const filterId = await filterService.newFilter(undefined, undefined, requestDetails, undefined, customTopic); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock .stub(cacheService, 'getAsync') @@ -524,7 +546,7 @@ describe('Filter API Test Suite', async function () { }, }); - const logs = await filterService.getFilterLogs(filterId); + const logs = await filterService.getFilterLogs(filterId, requestDetails); expect(logs).to.not.be.empty; logs.every((log) => expect(log.topics).to.deep.equal(customTopic)); @@ -539,7 +561,7 @@ describe('Filter API Test Suite', async function () { filterService.getFilterChanges, true, filterService, - [nonExistingFilterId], + [nonExistingFilterId, requestDetails], ); }); @@ -550,7 +572,7 @@ describe('Filter API Test Suite', async function () { filterService.getFilterChanges, true, filterService, - [nonExistingFilterId], + [nonExistingFilterId, requestDetails], ); }); @@ -568,13 +590,12 @@ describe('Filter API Test Suite', async function () { .reply(200, { blocks: [] }); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${existingFilterId}`; - cacheService.set( + await cacheService.set( cacheKey, blockFilterObject, filterService.ethGetFilterChanges, + requestDetails, constants.FILTER.TTL, - undefined, - true, ); cacheMock .stub(cacheService, 'getAsync') @@ -585,7 +606,7 @@ describe('Filter API Test Suite', async function () { .onThirdCall() .returns({ ...blockFilterObject, lastQueried: defaultBlock.number + 4 }); - const result = await filterService.getFilterChanges(existingFilterId); + const result = await filterService.getFilterChanges(existingFilterId, requestDetails); expect(result).to.exist; expect(result.length).to.eq(3, 'returns correct number of blocks'); @@ -593,7 +614,7 @@ describe('Filter API Test Suite', async function () { expect(result[1]).to.eq('0x2'); expect(result[2]).to.eq('0x3'); - const secondResult = await filterService.getFilterChanges(existingFilterId); + const secondResult = await filterService.getFilterChanges(existingFilterId, requestDetails); expect(secondResult).to.exist; expect(secondResult.length).to.eq(0, 'second call returns no block hashes'); }); @@ -609,13 +630,12 @@ describe('Filter API Test Suite', async function () { .reply(200, { blocks: [] }); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${existingFilterId}`; - cacheService.set( + await cacheService.set( cacheKey, blockFilterObject, filterService.ethGetFilterChanges, + requestDetails, constants.FILTER.TTL, - undefined, - true, ); cacheMock .stub(cacheService, 'getAsync') @@ -624,10 +644,10 @@ describe('Filter API Test Suite', async function () { .onSecondCall() .returns({ ...blockFilterObject, lastQueried: defaultBlock.number + 1 }); - const resultCurrentBlock = await filterService.getFilterChanges(existingFilterId); + const resultCurrentBlock = await filterService.getFilterChanges(existingFilterId, requestDetails); expect(resultCurrentBlock).to.not.be.empty; - const resultSameBlock = await filterService.getFilterChanges(existingFilterId); + const resultSameBlock = await filterService.getFilterChanges(existingFilterId, requestDetails); expect(resultSameBlock).to.be.empty; }); @@ -653,7 +673,7 @@ describe('Filter API Test Suite', async function () { .reply(200, filteredLogs); restMock.onGet('blocks/1').reply(200, { ...defaultBlock, block_number: 1 }); - const filterId = await filterService.newFilter('0x1'); + const filterId = await filterService.newFilter('0x1', undefined, requestDetails); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock .stub(cacheService, 'getAsync') @@ -665,9 +685,9 @@ describe('Filter API Test Suite', async function () { }, }); - const logs = await filterService.getFilterChanges(filterId); + const logs = await filterService.getFilterChanges(filterId, requestDetails); expect(logs).to.not.be.empty; - logs.every((log) => expect(Number(log.blockNumber)).to.equal(9)); + logs.forEach((log) => expect(Number(log.blockNumber)).to.equal(9)); }); it('should return an empty set if there are no logs', async function () { @@ -679,7 +699,7 @@ describe('Filter API Test Suite', async function () { .reply(200, []); restMock.onGet('blocks/1').reply(200, { ...defaultBlock, block_number: 1 }); - const filterId = await filterService.newFilter('0x1'); + const filterId = await filterService.newFilter('0x1', undefined, requestDetails); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${filterId}`; cacheMock .stub(cacheService, 'getAsync') @@ -690,7 +710,7 @@ describe('Filter API Test Suite', async function () { fromBlock: 1, }, }); - const logs = await filterService.getFilterChanges(filterId); + const logs = await filterService.getFilterChanges(filterId, requestDetails); expect(logs).to.be.empty; }); @@ -701,17 +721,16 @@ describe('Filter API Test Suite', async function () { }); const cacheKey = `${constants.CACHE_KEY.FILTERID}_${existingFilterId}`; - cacheService.set( + await cacheService.set( cacheKey, blockFilterObject, filterService.ethGetFilterChanges, + requestDetails, constants.FILTER.TTL, - undefined, - true, ); cacheMock.stub(cacheService, 'getAsync').returns(blockFilterObject); - const blocks = await filterService.getFilterChanges(existingFilterId); + const blocks = await filterService.getFilterChanges(existingFilterId, requestDetails); expect(blocks).to.be.empty; }); }); diff --git a/packages/relay/tests/lib/services/hbarLimitService/hbarLimitService.spec.ts b/packages/relay/tests/lib/services/hbarLimitService/hbarLimitService.spec.ts index e5dace01b4..8cdd4be158 100644 --- a/packages/relay/tests/lib/services/hbarLimitService/hbarLimitService.spec.ts +++ b/packages/relay/tests/lib/services/hbarLimitService/hbarLimitService.spec.ts @@ -38,6 +38,7 @@ import { EthAddressHbarSpendingPlanNotFoundError, IPAddressHbarSpendingPlanNotFoundError, } from '../../../../src/lib/db/types/hbarLimiter/errors'; +import { RequestDetails } from '../../../../src/lib/types'; chai.use(chaiAsPromised); @@ -53,6 +54,8 @@ describe('HbarLimitService', function () { const mockRequestId = getRequestId(); const mockPlanId = uuidV4(randomBytes(16)); + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: mockIpAddress }); + let hbarLimitService: HbarLimitService; let hbarSpendingPlanRepositoryStub: sinon.SinonStubbedInstance; let ethAddressHbarSpendingPlanRepositoryStub: sinon.SinonStubbedInstance; @@ -113,7 +116,7 @@ describe('HbarLimitService', function () { }); it('should reset the spentToday field of all spending plans', async function () { - await hbarLimitService.resetLimiter(); + await hbarLimitService.resetLimiter(requestDetails); expect(hbarSpendingPlanRepositoryStub.resetAllSpentTodayEntries.called).to.be.true; }); @@ -121,27 +124,27 @@ describe('HbarLimitService', function () { // @ts-ignore hbarLimitService.remainingBudget = 1000; const setSpy = sinon.spy(hbarLimitService['hbarLimitRemainingGauge'], 'set'); - await hbarLimitService.resetLimiter(); + await hbarLimitService.resetLimiter(requestDetails); expect(hbarLimitService['remainingBudget']).to.equal(totalBudget); expect(setSpy.calledOnceWith(totalBudget)).to.be.true; }); it('should reset the daily unique spending plans counter', async function () { const spies = createSpiesForMetricsReset('dailyUniqueSpendingPlansCounter'); - await hbarLimitService.resetLimiter(); + await hbarLimitService.resetLimiter(requestDetails); spies.forEach((spy) => sinon.assert.calledOnce(spy)); }); it('should reset the average daily spending plan usages gauge', async function () { const spies = createSpiesForMetricsReset('averageDailySpendingPlanUsagesGauge'); - await hbarLimitService.resetLimiter(); + await hbarLimitService.resetLimiter(requestDetails); spies.forEach((spy) => sinon.assert.calledOnce(spy)); }); it('should set the reset date to the next day at midnight', async function () { const tomorrow = new Date(Date.now() + HbarLimitService.ONE_DAY_IN_MILLIS); const expectedResetDate = new Date(tomorrow.setHours(0, 0, 0, 0)); - await hbarLimitService.resetLimiter(); + await hbarLimitService.resetLimiter(requestDetails); expect(hbarLimitService['reset']).to.deep.equal(expectedResetDate); }); }); @@ -151,7 +154,7 @@ describe('HbarLimitService', function () { it('should return true if the total daily budget is exceeded', async function () { // @ts-ignore hbarLimitService.remainingBudget = 0; - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.true; }); @@ -162,8 +165,7 @@ describe('HbarLimitService', function () { mode, methodName, mockEthAddress, - mockRequestId, - mockIpAddress, + requestDetails, mockEstimatedTxFee, ); expect(result).to.be.true; @@ -176,7 +178,7 @@ describe('HbarLimitService', function () { hbarSpendingPlanRepositoryStub.create.resolves(newSpendingPlan); ethAddressHbarSpendingPlanRepositoryStub.save.resolves(); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.false; expect(hbarSpendingPlanRepositoryStub.create.calledOnce).to.be.true; @@ -189,8 +191,9 @@ describe('HbarLimitService', function () { ).to.be.true; }); - it('should return false if ethAddress is null or empty', async function () { - const result = await hbarLimitService.shouldLimit(mode, methodName, ''); + it('should return false if ethAddress and ipAddress is empty string', async function () { + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: '' }); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; }); @@ -202,7 +205,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.true; }); @@ -215,7 +218,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.false; }); @@ -228,7 +231,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.true; }); @@ -248,8 +251,7 @@ describe('HbarLimitService', function () { mode, methodName, mockEthAddress, - mockRequestId, - mockIpAddress, + requestDetails, mockEstimatedTxFee, ); @@ -267,7 +269,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.false; }); @@ -283,7 +285,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, mockEthAddress, requestDetails); expect(result).to.be.false; }); @@ -293,21 +295,14 @@ describe('HbarLimitService', function () { it('should return true if the total daily budget is exceeded', async function () { // @ts-ignore hbarLimitService.remainingBudget = 0; - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.true; }); it('should return true when remainingBudget < estimatedTxFee ', async function () { // @ts-ignore hbarLimitService.remainingBudget = mockEstimatedTxFee - 1; - const result = await hbarLimitService.shouldLimit( - mode, - methodName, - '', - mockRequestId, - mockIpAddress, - mockEstimatedTxFee, - ); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails, mockEstimatedTxFee); expect(result).to.be.true; }); @@ -318,7 +313,8 @@ describe('HbarLimitService', function () { hbarSpendingPlanRepositoryStub.create.resolves(spendingPlan); ipAddressHbarSpendingPlanRepositoryStub.save.resolves(); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: mockIpAddress }); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; expect(hbarSpendingPlanRepositoryStub.create.calledOnce).to.be.true; @@ -332,7 +328,8 @@ describe('HbarLimitService', function () { }); it('should return false if ipAddress is null or empty', async function () { - const result = await hbarLimitService.shouldLimit(mode, methodName, '', ''); + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: '' }); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; }); @@ -344,7 +341,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.true; }); @@ -357,7 +354,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; }); @@ -370,7 +367,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.true; }); @@ -386,14 +383,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit( - mode, - methodName, - '', - mockRequestId, - mockIpAddress, - mockEstimatedTxFee, - ); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails, mockEstimatedTxFee); expect(result).to.be.true; }); @@ -409,7 +399,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; }); @@ -425,7 +415,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService.shouldLimit(mode, methodName, '', mockIpAddress); + const result = await hbarLimitService.shouldLimit(mode, methodName, '', requestDetails); expect(result).to.be.false; }); @@ -434,14 +424,15 @@ describe('HbarLimitService', function () { describe('getSpendingPlan', function () { it(`should return null if neither ethAddress nor ipAddress is provided`, async function () { - const ipAddresses = ['', null, undefined]; - const ethAddresses = ['', null, undefined]; + const ipAddresses = ['']; + const ethAddresses = ['']; const testCases = ethAddresses.flatMap((ethAddress) => ipAddresses.map((ipAddress) => ({ ethAddress, ipAddress })), ); for (const { ethAddress, ipAddress } of testCases) { // @ts-ignore - const result = await hbarLimitService['getSpendingPlan'](ethAddress, ipAddress); + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: ipAddress }); + const result = await hbarLimitService['getSpendingPlan'](ethAddress, requestDetails); expect(result).to.be.null; } }); @@ -454,7 +445,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService['getSpendingPlan'](mockEthAddress); + const result = await hbarLimitService['getSpendingPlan'](mockEthAddress, requestDetails); expect(result).to.deep.equal(spendingPlan); }); @@ -467,7 +458,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService['getSpendingPlan']('', mockIpAddress); + const result = await hbarLimitService['getSpendingPlan']('', requestDetails); expect(result).to.deep.equal(spendingPlan); }); @@ -476,7 +467,7 @@ describe('HbarLimitService', function () { const error = new EthAddressHbarSpendingPlanNotFoundError(mockEthAddress); ethAddressHbarSpendingPlanRepositoryStub.findByAddress.rejects(error); - const result = await hbarLimitService['getSpendingPlan'](mockEthAddress); + const result = await hbarLimitService['getSpendingPlan'](mockEthAddress, requestDetails); expect(result).to.be.null; }); @@ -485,7 +476,7 @@ describe('HbarLimitService', function () { const error = new IPAddressHbarSpendingPlanNotFoundError(mockIpAddress); ipAddressHbarSpendingPlanRepositoryStub.findByAddress.rejects(error); - const result = await hbarLimitService['getSpendingPlan']('', mockIpAddress); + const result = await hbarLimitService['getSpendingPlan']('', requestDetails); expect(result).to.be.null; }); @@ -493,7 +484,7 @@ describe('HbarLimitService', function () { describe('getSpendingPlanByEthAddress', function () { const testGetSpendingPlanByEthAddressError = async (error: Error, errorClass: any) => { - const result = hbarLimitService['getSpendingPlanByEthAddress'](mockEthAddress); + const result = hbarLimitService['getSpendingPlanByEthAddress'](mockEthAddress, requestDetails); await expect(result).to.be.eventually.rejectedWith(errorClass, error.message); }; @@ -531,7 +522,7 @@ describe('HbarLimitService', function () { }); hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(spendingPlan); - const result = await hbarLimitService['getSpendingPlanByEthAddress'](mockEthAddress); + const result = await hbarLimitService['getSpendingPlanByEthAddress'](mockEthAddress, requestDetails); expect(result).to.deep.equal(spendingPlan); }); @@ -539,11 +530,13 @@ describe('HbarLimitService', function () { describe('createBasicSpendingPlan', function () { const testCreateBasicSpendingPlan = async (ethAddress: string, ipAddress?: string) => { + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: ipAddress ? ipAddress : '' }); + console.log('requestDetails', requestDetails); const newSpendingPlan = createSpendingPlan(mockPlanId); hbarSpendingPlanRepositoryStub.create.resolves(newSpendingPlan); ethAddressHbarSpendingPlanRepositoryStub.save.resolves(); - const result = await hbarLimitService['createBasicSpendingPlan'](ethAddress, ipAddress); + const result = await hbarLimitService['createBasicSpendingPlan'](ethAddress, requestDetails); expect(result).to.deep.equal(newSpendingPlan); expect(hbarSpendingPlanRepositoryStub.create.calledOnce).to.be.true; @@ -612,7 +605,7 @@ describe('HbarLimitService', function () { 'updateAverageDailyUsagePerSubscriptionType', ); - await hbarLimitService.addExpense(expense, ethAddress, ipAddress); + await hbarLimitService.addExpense(expense, ethAddress, requestDetails); expect(hbarSpendingPlanRepositoryStub.addAmountToSpentToday.calledOnceWith(mockPlanId, expense)).to.be.true; // @ts-ignore @@ -628,15 +621,16 @@ describe('HbarLimitService', function () { sinon.assert.calledOnceWithExactly(incDailyUniqueSpendingPlansCounterSpy, 1); }; - it('should throw an error if neither ethAddress nor ipAddress is provided', async function () { - const ipAddresses = ['', null, undefined]; - const ethAddresses = ['', null, undefined]; + it('should throw an error if empty ethAddress or ipAddress is provided', async function () { + const ipAddresses = ['']; + const ethAddresses = ['']; const testCases = ethAddresses.flatMap((ethAddress) => ipAddresses.map((ipAddress) => ({ ethAddress, ipAddress })), ); for (const { ethAddress, ipAddress } of testCases) { + const requestDetails = new RequestDetails({ requestId: 'hbarLimterTest', ipAddress: ipAddress }); // @ts-ignore - await expect(hbarLimitService.addExpense(100, ethAddress, ipAddress)).to.be.eventually.rejectedWith( + await expect(hbarLimitService.addExpense(100, ethAddress, requestDetails)).to.be.eventually.rejectedWith( 'Cannot add expense without an eth address or ip address', ); } @@ -650,7 +644,7 @@ describe('HbarLimitService', function () { ); ethAddressHbarSpendingPlanRepositoryStub.save.resolves(); - await hbarLimitService.addExpense(100, mockEthAddress); + await hbarLimitService.addExpense(100, mockEthAddress, requestDetails); expect(hbarSpendingPlanRepositoryStub.create.calledOnce).to.be.true; expect(ethAddressHbarSpendingPlanRepositoryStub.save.calledOnce).to.be.true; @@ -676,7 +670,7 @@ describe('HbarLimitService', function () { hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(createSpendingPlan(mockPlanId)); hbarSpendingPlanRepositoryStub.addAmountToSpentToday.rejects(new Error('Failed to add expense')); - await expect(hbarLimitService.addExpense(100, mockEthAddress)).to.be.eventually.rejectedWith( + await expect(hbarLimitService.addExpense(100, mockEthAddress, requestDetails)).to.be.eventually.rejectedWith( 'Failed to add expense', ); }); @@ -686,7 +680,9 @@ describe('HbarLimitService', function () { const testIsDailyBudgetExceeded = async (remainingBudget: number, expected: boolean) => { // @ts-ignore hbarLimitService.remainingBudget = remainingBudget; - await expect(hbarLimitService['isDailyBudgetExceeded'](mode, methodName)).to.eventually.equal(expected); + await expect( + hbarLimitService['isDailyBudgetExceeded'](mode, methodName, undefined, requestDetails), + ).to.eventually.equal(expected); }; it('should return true when the remaining budget is zero', async function () { @@ -705,7 +701,7 @@ describe('HbarLimitService', function () { hbarSpendingPlanRepositoryStub.findByIdWithDetails.resolves(createSpendingPlan(mockPlanId)); hbarSpendingPlanRepositoryStub.addAmountToSpentToday.rejects(new Error('Failed to add expense')); - await expect(hbarLimitService.addExpense(100, mockEthAddress)).to.be.eventually.rejectedWith( + await expect(hbarLimitService.addExpense(100, mockEthAddress, requestDetails)).to.be.eventually.rejectedWith( 'Failed to add expense', ); }); diff --git a/packages/relay/tests/lib/services/metricService/metricService.spec.ts b/packages/relay/tests/lib/services/metricService/metricService.spec.ts index f90d8f96fa..2334dc0d5a 100644 --- a/packages/relay/tests/lib/services/metricService/metricService.spec.ts +++ b/packages/relay/tests/lib/services/metricService/metricService.spec.ts @@ -31,11 +31,12 @@ import { Utils } from '../../../../src/utils'; import constants from '../../../../src/lib/constants'; import HbarLimit from '../../../../src/lib/hbarlimiter'; import { MirrorNodeClient, SDKClient } from '../../../../src/lib/clients'; -import { calculateTxRecordChargeAmount, getRequestId } from '../../../helpers'; +import { calculateTxRecordChargeAmount } from '../../../helpers'; import MetricService from '../../../../src/lib/services/metricService/metricService'; import { CacheService } from '../../../../src/lib/services/cacheService/cacheService'; -import { IExecuteQueryEventPayload, IExecuteTransactionEventPayload } from '../../../../src/lib/types/events'; +import { IExecuteQueryEventPayload, IExecuteTransactionEventPayload } from '../../../../src/lib/types'; import { Hbar, Long, Status, Client, AccountId, TransactionRecord, TransactionRecordQuery } from '@hashgraph/sdk'; +import { RequestDetails } from '../../../../src/lib/types'; config({ path: resolve(__dirname, '../../../test.env') }); const registry = new Registry(); @@ -50,6 +51,7 @@ describe('Metric Service', function () { let metricService: MetricService; let mirrorNodeClient: MirrorNodeClient; + const requestDetails = new RequestDetails({ requestId: 'metricServiceTest', ipAddress: '0.0.0.0' }); const mockedTxFee = 36900000; const operatorAccountId = `0.0.1022`; const mockedCallerName = 'caller_name'; @@ -157,10 +159,10 @@ describe('Metric Service', function () { const mockedExecuteTransactionEventPayload: IExecuteTransactionEventPayload = { transactionId: mockedTransactionId, callerName: mockedCallerName, - requestId: getRequestId(), txConstructorName: mockedConstructorName, operatorAccountId, interactingEntity: mockedInteractingEntity, + requestDetails, }; it('Should execute captureTransactionMetrics() by retrieving transaction record from MIRROR NODE client', async () => { @@ -178,14 +180,14 @@ describe('Metric Service', function () { expect(originalBudget - updatedBudget).to.eq(mockedTxFee); // validate cost metrics - // @ts-ignore - const costMetricObject = (await metricService.getCostMetric().get()).values.find( + const costMetricObject = (await metricService['consensusNodeClientHistogramCost'].get()).values.find( (metric) => metric.metricName === metricHistogramCostSumTitle, - )!; - expect(costMetricObject.metricName).to.eq(metricHistogramCostSumTitle); - expect(costMetricObject.labels.caller).to.eq(mockedCallerName); - expect(costMetricObject.labels.interactingEntity).to.eq(mockedInteractingEntity); - expect(costMetricObject.value).to.eq(mockedTxFee); + ); + expect(costMetricObject).to.not.be.undefined; + expect(costMetricObject!.metricName).to.eq(metricHistogramCostSumTitle); + expect(costMetricObject!.labels.caller).to.eq(mockedCallerName); + expect(costMetricObject!.labels.interactingEntity).to.eq(mockedInteractingEntity); + expect(costMetricObject!.value).to.eq(mockedTxFee); }); it('Should execute captureTransactionMetrics() by retrieving transaction record from CONSENSUS NODE client', async () => { @@ -209,7 +211,7 @@ describe('Metric Service', function () { // validate cost metric // @ts-ignore - const metricObjects = await metricService.getCostMetric().get(); + const metricObjects = await metricService['consensusNodeClientHistogramCost'].get(); const txRecordFeeMetricObject = metricObjects.values.find((metric) => { return ( metric.labels.mode === constants.EXECUTION_MODE.RECORD && metric.metricName === metricHistogramCostSumTitle @@ -234,7 +236,7 @@ describe('Metric Service', function () { // validate gas metric // @ts-ignore - const gasMetricObject = (await metricService.getGasFeeMetric().get()).values.find( + const gasMetricObject = (await metricService['consensusNodeClientHistogramGasFee'].get()).values.find( (metric) => metric.metricName === metricHistogramGasFeeSumTitle, )!; @@ -273,7 +275,7 @@ describe('Metric Service', function () { // validate cost metric // @ts-ignore - const metricObjects = await metricService.getCostMetric().get(); + const metricObjects = await metricService['consensusNodeClientHistogramCost'].get(); const txRecordFeeMetricObject = metricObjects.values.find((metric) => { return ( metric.labels.mode === constants.EXECUTION_MODE.RECORD && metric.metricName === metricHistogramCostSumTitle @@ -298,7 +300,7 @@ describe('Metric Service', function () { // validate gas metric // @ts-ignore - const gasMetricObject = (await metricService.getGasFeeMetric().get()).values.find( + const gasMetricObject = (await metricService['consensusNodeClientHistogramGasFee'].get()).values.find( (metric) => metric.metricName === metricHistogramGasFeeSumTitle, )!; @@ -322,7 +324,7 @@ describe('Metric Service', function () { gasUsed: mockedGasUsed, interactingEntity: mockedInteractingEntity, status: 'SUCCESS', - requestId: getRequestId(), + requestDetails, }; it('should execute addExpenseAndCaptureMetrics() to capture metrics in HBAR limiter and metric registry', async () => { const originalBudget = hbarLimiter.getRemainingBudget(); @@ -336,7 +338,7 @@ describe('Metric Service', function () { // validate cost metrics // @ts-ignore - const costMetricObject = (await metricService.getCostMetric().get()).values.find( + const costMetricObject = (await metricService['consensusNodeClientHistogramCost'].get()).values.find( (metric) => metric.metricName === metricHistogramCostSumTitle, )!; expect(costMetricObject.metricName).to.eq(metricHistogramCostSumTitle); @@ -346,7 +348,7 @@ describe('Metric Service', function () { // validate gas metric // @ts-ignore - const gasMetricObject = (await metricService.getGasFeeMetric().get()).values.find( + const gasMetricObject = (await metricService['consensusNodeClientHistogramGasFee'].get()).values.find( (metric) => metric.metricName === metricHistogramGasFeeSumTitle, )!; @@ -373,7 +375,7 @@ describe('Metric Service', function () { // validate cost metrics // @ts-ignore - const costMetricObject = (await metricService.getCostMetric().get()).values.find( + const costMetricObject = (await metricService['consensusNodeClientHistogramCost'].get()).values.find( (metric) => metric.metricName === metricHistogramCostSumTitle, )!; expect(costMetricObject.metricName).to.eq(metricHistogramCostSumTitle); @@ -383,7 +385,7 @@ describe('Metric Service', function () { // validate gas metric // @ts-ignore - const gasMetricObject = (await metricService.getGasFeeMetric().get()).values.find( + const gasMetricObject = (await metricService['consensusNodeClientHistogramGasFee'].get()).values.find( (metric) => metric.metricName === metricHistogramGasFeeSumTitle, )!; diff --git a/packages/server/src/koaJsonRpc/index.ts b/packages/server/src/koaJsonRpc/index.ts index f32313dc30..c68b01b1cf 100644 --- a/packages/server/src/koaJsonRpc/index.ts +++ b/packages/server/src/koaJsonRpc/index.ts @@ -37,6 +37,7 @@ import { import Koa from 'koa'; import { Histogram, Registry } from 'prom-client'; import { JsonRpcError, predefined } from '@hashgraph/json-rpc-relay'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; import { RpcErrorCodeToStatusMap } from './lib/HttpStatusCodeAndMessage'; import { getBatchRequestsEnabled, @@ -72,10 +73,12 @@ export default class KoaJsonRpc { private readonly methodResponseHistogram: Histogram; private requestId: string; + private requestIpAddress: string; constructor(logger: Logger, register: Registry, opts?: { limit: string | null }) { this.koaApp = new Koa(); this.requestId = ''; + this.requestIpAddress = ''; this.registry = Object.create(null); this.registryTotal = Object.create(null); this.methodConfig = methodConfiguration; @@ -106,6 +109,7 @@ export default class KoaJsonRpc { rpcApp(): (ctx: Koa.Context, _next: Koa.Next) => Promise { return async (ctx: Koa.Context, _next: Koa.Next) => { this.requestId = ctx.state.reqId; + this.requestIpAddress = ctx.request.ip; ctx.set(REQUEST_ID_HEADER_NAME, this.requestId); if (ctx.request.method !== 'POST') { @@ -257,6 +261,14 @@ export default class KoaJsonRpc { return this.requestId; } + getRequestIpAddress(): string { + return this.requestIpAddress; + } + + getRequestDetails(): RequestDetails { + return new RequestDetails({ requestId: this.requestId, ipAddress: this.requestIpAddress }); + } + hasInvalidRequestId(body: IJsonRpcRequest): boolean { const hasId = hasOwnProperty(body, 'id'); if (this.requestIdIsOptional && !hasId) { diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index d0a42a08fa..db557c0613 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -19,7 +19,7 @@ */ import { JsonRpcError, MirrorNodeClientError, predefined, Relay, RelayImpl } from '@hashgraph/json-rpc-relay'; -import { ITracerConfig } from '@hashgraph/json-rpc-relay/src/lib/types'; +import { ITracerConfig, RequestDetails } from '@hashgraph/json-rpc-relay/src/lib/types'; import { collectDefaultMetrics, Histogram, Registry } from 'prom-client'; import KoaJsonRpc from './koaJsonRpc'; import { TracerType, TYPES, Validator } from './validator'; @@ -114,7 +114,7 @@ app.getKoaApp().use(async (ctx, next) => { app.getKoaApp().use(async (ctx, next) => { if (ctx.url === '/health/readiness') { try { - const result = relay.eth().chainId(); + const result = relay.eth().chainId(app.getRequestDetails()); if (result.indexOf('0x12') >= 0) { ctx.status = 200; ctx.body = 'OK'; @@ -197,26 +197,26 @@ app.getKoaApp().use(async (ctx, next) => { return next(); }); -const logAndHandleResponse = async (methodName: any, methodParams: any, methodFunction: any) => { - const requestId = app.getRequestId(); - const requestIdPrefix = requestId ? formatRequestIdMessage(requestId) : ''; +const logAndHandleResponse = async (methodName: string, methodParams: any[], methodFunction: any) => { + const requestDetails = app.getRequestDetails(); try { const methodValidations = Validator.METHODS[methodName]; if (methodValidations) { logger.debug( - `${requestIdPrefix} Validating method parameters for ${methodName}, params: ${JSON.stringify(methodParams)}`, + `${requestDetails.formattedRequestId} Validating method parameters for ${methodName}, params: ${JSON.stringify( + methodParams, + )}`, ); Validator.validateParams(methodParams, methodValidations); } - - const response = await methodFunction(requestIdPrefix); + const response = await methodFunction(requestDetails); if (response instanceof JsonRpcError) { // log error only if it is not a contract revert, otherwise log it as debug if (response.code === predefined.CONTRACT_REVERT().code) { - logger.debug(`${requestIdPrefix} ${response.message}`); + logger.debug(`${requestDetails.formattedRequestId} ${response.message}`); } else { - logger.error(`${requestIdPrefix} ${response.message}`); + logger.error(`${requestDetails.formattedRequestId} ${response.message}`); } return new JsonRpcError( @@ -225,7 +225,7 @@ const logAndHandleResponse = async (methodName: any, methodParams: any, methodFu message: response.message, data: response.data, }, - requestId, + requestDetails.requestId, ); } return response; @@ -238,17 +238,17 @@ const logAndHandleResponse = async (methodName: any, methodParams: any, methodFu } else if (e instanceof JsonRpcError) { error = e; } else { - logger.error(`${requestIdPrefix} ${e.message}`); + logger.error(`${requestDetails.formattedRequestId} ${e.message}`); } - logger.error(`${requestIdPrefix} ${error.message}`); + logger.error(`${requestDetails.formattedRequestId} ${error.message}`); return new JsonRpcError( { code: error.code, message: error.message, data: error.data, }, - requestId, + requestDetails.requestId, ); } }; @@ -273,7 +273,7 @@ app.useRpc('net_version', async () => { * returns: Block number - hex encoded integer */ app.useRpc('eth_blockNumber', async () => { - return logAndHandleResponse('eth_blockNumber', [], (requestId) => relay.eth().blockNumber(requestId)); + return logAndHandleResponse('eth_blockNumber', [], (requestDetails) => relay.eth().blockNumber(requestDetails)); }); /** @@ -288,8 +288,8 @@ app.useRpc('eth_estimateGas', async (params: any) => { delete params[0].data; } - return logAndHandleResponse('eth_estimateGas', params, (requestId) => - relay.eth().estimateGas(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_estimateGas', params, (requestDetails) => + relay.eth().estimateGas(params?.[0], params?.[1], requestDetails), ); }); @@ -301,8 +301,8 @@ app.useRpc('eth_estimateGas', async (params: any) => { * returns: Balance - hex encoded integer */ app.useRpc('eth_getBalance', async (params: any) => { - return logAndHandleResponse('eth_getBalance', params, (requestId) => - relay.eth().getBalance(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_getBalance', params, (requestDetails) => + relay.eth().getBalance(params?.[0], params?.[1], requestDetails), ); }); @@ -314,8 +314,8 @@ app.useRpc('eth_getBalance', async (params: any) => { * returns: Bytecode - hex encoded bytes */ app.useRpc('eth_getCode', async (params: any) => { - return logAndHandleResponse('eth_getCode', params, (requestId) => - relay.eth().getCode(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_getCode', params, (requestDetails) => + relay.eth().getCode(params?.[0], params?.[1], requestDetails), ); }); @@ -325,7 +325,7 @@ app.useRpc('eth_getCode', async (params: any) => { * returns: Chain ID - integer */ app.useRpc('eth_chainId', async () => { - return logAndHandleResponse('eth_chainId', [], (requestId) => relay.eth().chainId(requestId)); + return logAndHandleResponse('eth_chainId', [], (requestDetails) => relay.eth().chainId(requestDetails)); }); /** @@ -336,8 +336,8 @@ app.useRpc('eth_chainId', async () => { * returns: Block object */ app.useRpc('eth_getBlockByNumber', async (params: any) => { - return logAndHandleResponse('eth_getBlockByNumber', params, (requestId) => - relay.eth().getBlockByNumber(params?.[0], Boolean(params?.[1]), requestId), + return logAndHandleResponse('eth_getBlockByNumber', params, (requestDetails) => + relay.eth().getBlockByNumber(params?.[0], Boolean(params?.[1]), requestDetails), ); }); @@ -349,8 +349,8 @@ app.useRpc('eth_getBlockByNumber', async (params: any) => { * returns: Block object */ app.useRpc('eth_getBlockByHash', async (params: any) => { - return logAndHandleResponse('eth_getBlockByHash', params, (requestId) => - relay.eth().getBlockByHash(params?.[0], Boolean(params?.[1]), requestId), + return logAndHandleResponse('eth_getBlockByHash', params, (requestDetails) => + relay.eth().getBlockByHash(params?.[0], Boolean(params?.[1]), requestDetails), ); }); @@ -360,7 +360,7 @@ app.useRpc('eth_getBlockByHash', async (params: any) => { * returns: Gas price - hex encoded integer */ app.useRpc('eth_gasPrice', async () => { - return logAndHandleResponse('eth_gasPrice', [], (requestId) => relay.eth().gasPrice(requestId)); + return logAndHandleResponse('eth_gasPrice', [], (requestDetails) => relay.eth().gasPrice(requestDetails)); }); /** @@ -371,8 +371,8 @@ app.useRpc('eth_gasPrice', async () => { * returns: Transaction count - hex encoded integer */ app.useRpc('eth_getTransactionCount', async (params: any) => { - return logAndHandleResponse('eth_getTransactionCount', params, (requestId) => - relay.eth().getTransactionCount(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_getTransactionCount', params, (requestDetails) => + relay.eth().getTransactionCount(params?.[0], params?.[1], requestDetails), ); }); @@ -383,7 +383,9 @@ app.useRpc('eth_getTransactionCount', async (params: any) => { * returns: Value - hex encoded bytes */ app.useRpc('eth_call', async (params: any) => { - return logAndHandleResponse('eth_call', params, (requestId) => relay.eth().call(params?.[0], params?.[1], requestId)); + return logAndHandleResponse('eth_call', params, (requestDetails) => + relay.eth().call(params?.[0], params?.[1], requestDetails), + ); }); /** @@ -393,8 +395,8 @@ app.useRpc('eth_call', async (params: any) => { * returns: Transaction hash - 32 byte hex value */ app.useRpc('eth_sendRawTransaction', async (params: any) => { - return logAndHandleResponse('eth_sendRawTransaction', params, (requestId) => - relay.eth().sendRawTransaction(params?.[0], requestId), + return logAndHandleResponse('eth_sendRawTransaction', params, (requestDetails) => + relay.eth().sendRawTransaction(params?.[0], requestDetails), ); }); @@ -405,8 +407,8 @@ app.useRpc('eth_sendRawTransaction', async (params: any) => { * returns: Transaction Receipt - object */ app.useRpc('eth_getTransactionReceipt', async (params: any) => { - return logAndHandleResponse('eth_getTransactionReceipt', params, (requestId) => - relay.eth().getTransactionReceipt(params?.[0], requestId), + return logAndHandleResponse('eth_getTransactionReceipt', params, (requestDetails) => + relay.eth().getTransactionReceipt(params?.[0], requestDetails), ); }); @@ -420,7 +422,7 @@ app.useRpc('web3_clientVersion', async () => { * returns: Accounts - hex encoded address */ app.useRpc('eth_accounts', async () => { - return logAndHandleResponse('eth_accounts', [], (requestId) => relay.eth().accounts(requestId)); + return logAndHandleResponse('eth_accounts', [], (requestDetails) => relay.eth().accounts(requestDetails)); }); /** @@ -430,8 +432,8 @@ app.useRpc('eth_accounts', async () => { * returns: Transaction Object */ app.useRpc('eth_getTransactionByHash', async (params: any) => { - return logAndHandleResponse('eth_getTransactionByHash', params, (requestId) => - relay.eth().getTransactionByHash(params[0], requestId), + return logAndHandleResponse('eth_getTransactionByHash', params, (requestDetails) => + relay.eth().getTransactionByHash(params[0], requestDetails), ); }); @@ -448,8 +450,8 @@ app.useRpc('eth_getTransactionByHash', async (params: any) => { * - reward - Array of effective priority fee per gas data. */ app.useRpc('eth_feeHistory', async (params: any) => { - return logAndHandleResponse('eth_feeHistory', params, (requestId) => - relay.eth().feeHistory(Number(params?.[0]), params?.[1], params?.[2], requestId), + return logAndHandleResponse('eth_feeHistory', params, (requestDetails) => + relay.eth().feeHistory(Number(params?.[0]), params?.[1], params?.[2], requestDetails), ); }); @@ -460,8 +462,8 @@ app.useRpc('eth_feeHistory', async (params: any) => { * returns: Block Transaction Count - Hex encoded integer */ app.useRpc('eth_getBlockTransactionCountByHash', async (params: any) => { - return logAndHandleResponse('eth_getBlockTransactionCountByHash', params, (requestId) => - relay.eth().getBlockTransactionCountByHash(params?.[0], requestId), + return logAndHandleResponse('eth_getBlockTransactionCountByHash', params, (requestDetails) => + relay.eth().getBlockTransactionCountByHash(params?.[0], requestDetails), ); }); @@ -472,8 +474,8 @@ app.useRpc('eth_getBlockTransactionCountByHash', async (params: any) => { * returns: Block Transaction Count - Hex encoded integer */ app.useRpc('eth_getBlockTransactionCountByNumber', async (params: any) => { - return logAndHandleResponse('eth_getBlockTransactionCountByNumber', params, (requestId) => - relay.eth().getBlockTransactionCountByNumber(params?.[0], requestId), + return logAndHandleResponse('eth_getBlockTransactionCountByNumber', params, (requestDetails) => + relay.eth().getBlockTransactionCountByNumber(params?.[0], requestDetails), ); }); @@ -486,8 +488,10 @@ app.useRpc('eth_getBlockTransactionCountByNumber', async (params: any) => { app.useRpc('eth_getLogs', async (params: any) => { const filter = params[0]; - return logAndHandleResponse('eth_getLogs', params, (requestId) => - relay.eth().getLogs(filter.blockHash, filter.fromBlock, filter.toBlock, filter.address, filter.topics, requestId), + return logAndHandleResponse('eth_getLogs', params, (requestDetails) => + relay + .eth() + .getLogs(filter.blockHash, filter.fromBlock, filter.toBlock, filter.address, filter.topics, requestDetails), ); }); @@ -500,8 +504,8 @@ app.useRpc('eth_getLogs', async (params: any) => { * returns: Value - The storage value */ app.useRpc('eth_getStorageAt', async (params: any) => { - return logAndHandleResponse('eth_getStorageAt', params, (requestId) => - relay.eth().getStorageAt(params?.[0], params?.[1], params?.[2], requestId), + return logAndHandleResponse('eth_getStorageAt', params, (requestDetails) => + relay.eth().getStorageAt(params?.[0], params?.[1], requestDetails, params?.[2]), ); }); @@ -513,8 +517,8 @@ app.useRpc('eth_getStorageAt', async (params: any) => { * returns: Transaction */ app.useRpc('eth_getTransactionByBlockHashAndIndex', async (params: any) => { - return logAndHandleResponse('eth_getTransactionByBlockHashAndIndex', params, (requestId) => - relay.eth().getTransactionByBlockHashAndIndex(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_getTransactionByBlockHashAndIndex', params, (requestDetails) => + relay.eth().getTransactionByBlockHashAndIndex(params?.[0], params?.[1], requestDetails), ); }); @@ -526,8 +530,8 @@ app.useRpc('eth_getTransactionByBlockHashAndIndex', async (params: any) => { * returns: Transaction */ app.useRpc('eth_getTransactionByBlockNumberAndIndex', async (params: any) => { - return logAndHandleResponse('eth_getTransactionByBlockNumberAndIndex', params, (requestId) => - relay.eth().getTransactionByBlockNumberAndIndex(params?.[0], params?.[1], requestId), + return logAndHandleResponse('eth_getTransactionByBlockNumberAndIndex', params, (requestDetails) => + relay.eth().getTransactionByBlockNumberAndIndex(params?.[0], params?.[1], requestDetails), ); }); @@ -541,8 +545,8 @@ app.useRpc('eth_getTransactionByBlockNumberAndIndex', async (params: any) => { * returns: null */ app.useRpc('eth_getUncleByBlockHashAndIndex', async () => { - return logAndHandleResponse('eth_getUncleByBlockHashAndIndex', [], (requestId) => - relay.eth().getUncleByBlockHashAndIndex(requestId), + return logAndHandleResponse('eth_getUncleByBlockHashAndIndex', [], (requestDetails) => + relay.eth().getUncleByBlockHashAndIndex(requestDetails), ); }); @@ -555,8 +559,8 @@ app.useRpc('eth_getUncleByBlockHashAndIndex', async () => { * returns: null */ app.useRpc('eth_getUncleByBlockNumberAndIndex', async () => { - return logAndHandleResponse('eth_getUncleByBlockNumberAndIndex', [], (requestId) => - relay.eth().getUncleByBlockNumberAndIndex(requestId), + return logAndHandleResponse('eth_getUncleByBlockNumberAndIndex', [], (requestDetails) => + relay.eth().getUncleByBlockNumberAndIndex(requestDetails), ); }); @@ -568,8 +572,8 @@ app.useRpc('eth_getUncleByBlockNumberAndIndex', async () => { * returns: 0x0 */ app.useRpc('eth_getUncleCountByBlockHash', async () => { - return logAndHandleResponse('eth_getUncleCountByBlockHash', [], (requestId) => - relay.eth().getUncleCountByBlockHash(requestId), + return logAndHandleResponse('eth_getUncleCountByBlockHash', [], (requestDetails) => + relay.eth().getUncleCountByBlockHash(requestDetails), ); }); @@ -581,8 +585,8 @@ app.useRpc('eth_getUncleCountByBlockHash', async () => { * returns: 0x0 */ app.useRpc('eth_getUncleCountByBlockNumber', async () => { - return logAndHandleResponse('eth_getUncleCountByBlockNumber', [], (requestId) => - relay.eth().getUncleCountByBlockNumber(requestId), + return logAndHandleResponse('eth_getUncleCountByBlockNumber', [], (requestDetails) => + relay.eth().getUncleCountByBlockNumber(requestDetails), ); }); @@ -593,7 +597,7 @@ app.useRpc('eth_getUncleCountByBlockNumber', async () => { * returns: code: -32000 */ app.useRpc('eth_getWork', async () => { - return logAndHandleResponse('eth_getWork', [], (requestId) => relay.eth().getWork(requestId)); + return logAndHandleResponse('eth_getWork', [], (requestDetails) => relay.eth().getWork(requestDetails)); }); /** @@ -604,7 +608,7 @@ app.useRpc('eth_getWork', async () => { * returns: 0x0 */ app.useRpc('eth_hashrate', async () => { - return logAndHandleResponse('eth_hashrate', [], (requestId) => relay.eth().hashrate(requestId)); + return logAndHandleResponse('eth_hashrate', [], (requestDetails) => relay.eth().hashrate(requestDetails)); }); /** @@ -615,7 +619,7 @@ app.useRpc('eth_hashrate', async () => { * returns: false */ app.useRpc('eth_mining', async () => { - return logAndHandleResponse('eth_mining', [], (requestId) => relay.eth().mining(requestId)); + return logAndHandleResponse('eth_mining', [], (requestDetails) => relay.eth().mining(requestDetails)); }); /** @@ -626,7 +630,7 @@ app.useRpc('eth_mining', async () => { * returns: false */ app.useRpc('eth_submitWork', async () => { - return logAndHandleResponse('eth_submitWork', [], (requestId) => relay.eth().submitWork(requestId)); + return logAndHandleResponse('eth_submitWork', [], (requestDetails) => relay.eth().submitWork(requestDetails)); }); /** @@ -636,7 +640,7 @@ app.useRpc('eth_submitWork', async () => { * returns: false */ app.useRpc('eth_syncing', async () => { - return logAndHandleResponse('eth_syncing', [], (requestId) => relay.eth().syncing(requestId)); + return logAndHandleResponse('eth_syncing', [], (requestDetails) => relay.eth().syncing(requestDetails)); }); /** @@ -657,8 +661,8 @@ app.useRpc('web3_client_version', async () => { * returns: 0x0 */ app.useRpc('eth_maxPriorityFeePerGas', async () => { - return logAndHandleResponse('eth_maxPriorityFeePerGas', [], (requestId) => - relay.eth().maxPriorityFeePerGas(requestId), + return logAndHandleResponse('eth_maxPriorityFeePerGas', [], (requestDetails) => + relay.eth().maxPriorityFeePerGas(requestDetails), ); }); @@ -667,7 +671,7 @@ app.useRpc('eth_maxPriorityFeePerGas', async () => { */ app.useRpc('debug_traceTransaction', async (params: any) => { - return logAndHandleResponse('debug_traceTransaction', params, (requestId: string) => { + return logAndHandleResponse('debug_traceTransaction', params, (requestDetails: RequestDetails) => { const transactionIdOrHash = params[0]; let tracer: TracerType = TracerType.OpcodeLogger; let tracerConfig: ITracerConfig = {}; @@ -689,7 +693,7 @@ app.useRpc('debug_traceTransaction', async (params: any) => { } } - return relay.eth().debugService().debug_traceTransaction(transactionIdOrHash, tracer, tracerConfig, requestId); + return relay.eth().debugService().debug_traceTransaction(transactionIdOrHash, tracer, tracerConfig, requestDetails); }); }); @@ -704,27 +708,27 @@ app.useRpc('debug_traceTransaction', async (params: any) => { */ app.useRpc('eth_newFilter', async (params: any) => { const filter = params[0]; - return logAndHandleResponse('eth_newFilter', [], (requestId) => + return logAndHandleResponse('eth_newFilter', [], (requestDetails) => relay .eth() .filterService() - .newFilter(filter?.fromBlock, filter?.toBlock, filter?.address, filter?.topics, requestId), + .newFilter(filter?.fromBlock, filter?.toBlock, requestDetails, filter?.address, filter?.topics), ); }); app.useRpc('eth_getFilterLogs', async (params: any) => { - return logAndHandleResponse('eth_getFilterLogs', params, (requestId) => + return logAndHandleResponse('eth_getFilterLogs', params, (requestDetails) => relay .eth() .filterService() - .getFilterLogs(params?.[0], requestId), + .getFilterLogs(params?.[0], requestDetails), ); }); app.useRpc('eth_getFilterChanges', async (params: any) => { const filterId = params[0]; - return logAndHandleResponse('eth_getFilterChanges', [], (requestId) => - relay.eth().filterService().getFilterChanges(filterId, requestId), + return logAndHandleResponse('eth_getFilterChanges', [], (requestDetails) => + relay.eth().filterService().getFilterChanges(filterId, requestDetails), ); }); @@ -734,8 +738,8 @@ app.useRpc('eth_getFilterChanges', async (params: any) => { * returns: string */ app.useRpc('eth_newBlockFilter', async (params: any) => { - return logAndHandleResponse('eth_newBlockFilter', [], (requestId) => - relay.eth().filterService().newBlockFilter(requestId), + return logAndHandleResponse('eth_newBlockFilter', [], (requestDetails) => + relay.eth().filterService().newBlockFilter(requestDetails), ); }); @@ -743,8 +747,8 @@ app.useRpc('eth_newBlockFilter', async (params: any) => { * Not Supported */ app.useRpc('eth_newPendingTransactionFilter', async () => { - return logAndHandleResponse('eth_newPendingTransactionFilter', [], (requestId) => - relay.eth().filterService().newPendingTransactionFilter(requestId), + return logAndHandleResponse('eth_newPendingTransactionFilter', [], (requestDetails) => + relay.eth().filterService().newPendingTransactionFilter(requestDetails), ); }); @@ -755,11 +759,11 @@ app.useRpc('eth_newPendingTransactionFilter', async () => { * returns: boolean */ app.useRpc('eth_uninstallFilter', async (params: any) => { - return logAndHandleResponse('eth_uninstallFilter', params, (requestId) => + return logAndHandleResponse('eth_uninstallFilter', params, (requestDetails) => relay .eth() .filterService() - .uninstallFilter(params?.[0], requestId), + .uninstallFilter(params?.[0], requestDetails), ); }); @@ -767,27 +771,33 @@ app.useRpc('eth_uninstallFilter', async (params: any) => { * Not supported */ app.useRpc('eth_submitHashrate', async () => { - return logAndHandleResponse('eth_submitHashrate', [], (requestId) => relay.eth().submitHashrate(requestId)); + return logAndHandleResponse('eth_submitHashrate', [], (requestDetails) => relay.eth().submitHashrate(requestDetails)); }); app.useRpc('eth_signTransaction', async () => { - return logAndHandleResponse('eth_signTransaction', [], (requestId) => relay.eth().signTransaction(requestId)); + return logAndHandleResponse('eth_signTransaction', [], (requestDetails) => + relay.eth().signTransaction(requestDetails), + ); }); app.useRpc('eth_sign', async () => { - return logAndHandleResponse('eth_sign', [], (requestId) => relay.eth().sign(requestId)); + return logAndHandleResponse('eth_sign', [], (requestDetails) => relay.eth().sign(requestDetails)); }); app.useRpc('eth_sendTransaction', async () => { - return logAndHandleResponse('eth_sendTransaction', [], (requestId) => relay.eth().sendTransaction(requestId)); + return logAndHandleResponse('eth_sendTransaction', [], (requestDetails) => + relay.eth().sendTransaction(requestDetails), + ); }); app.useRpc('eth_protocolVersion', async () => { - return logAndHandleResponse('eth_protocolVersion', [], (requestId) => relay.eth().protocolVersion(requestId)); + return logAndHandleResponse('eth_protocolVersion', [], (requestDetails) => + relay.eth().protocolVersion(requestDetails), + ); }); app.useRpc('eth_coinbase', async () => { - return logAndHandleResponse('eth_coinbase', [], (requestId) => relay.eth().coinbase(requestId)); + return logAndHandleResponse('eth_coinbase', [], (requestDetails) => relay.eth().coinbase(requestDetails)); }); const rpcApp = app.rpcApp(); diff --git a/packages/server/tests/acceptance/cacheService.spec.ts b/packages/server/tests/acceptance/cacheService.spec.ts index c734a1dccd..fb148c59da 100644 --- a/packages/server/tests/acceptance/cacheService.spec.ts +++ b/packages/server/tests/acceptance/cacheService.spec.ts @@ -21,6 +21,7 @@ import { expect } from 'chai'; import { CacheService } from '../../../../packages/relay/src/lib/services/cacheService/cacheService'; import { Registry } from 'prom-client'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; const registry = new Registry(); const DATA_LABEL_PREFIX = 'acceptance-test-'; @@ -32,6 +33,8 @@ const CALLING_METHOD = 'AcceptanceTest'; describe('@cache-service Acceptance Tests for shared cache', function () { let cacheService: CacheService; + const requestDetails = new RequestDetails({ requestId: 'cacheServiceTest', ipAddress: '0.0.0.0' }); + before(async () => { cacheService = new CacheService(global.logger, registry); await new Promise((r) => setTimeout(r, 1000)); @@ -40,22 +43,22 @@ describe('@cache-service Acceptance Tests for shared cache', function () { it('Correctly performs set, get and delete operations', async () => { const dataLabel = `${DATA_LABEL_PREFIX}1`; - await cacheService.set(dataLabel, DATA, CALLING_METHOD, undefined, undefined, true); + await cacheService.set(dataLabel, DATA, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - const cache = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const cache = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(cache).to.deep.eq(DATA, 'set method saves to shared cache'); - const cacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const cacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(cacheFromService).to.deep.eq(DATA, 'getAsync method reads correctly from shared cache'); - await cacheService.delete(dataLabel, CALLING_METHOD, undefined, true); + await cacheService.delete(dataLabel, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - const deletedCache = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const deletedCache = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(deletedCache).to.eq(null, 'the delete method correctly deletes from shared cache'); - const deletedCacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const deletedCacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(deletedCacheFromService).to.eq(null, 'getAsync method cannot read deleted cache'); }); @@ -63,18 +66,18 @@ describe('@cache-service Acceptance Tests for shared cache', function () { const ttl = 1000; const dataLabel = `${DATA_LABEL_PREFIX}2`; - await cacheService.set(dataLabel, DATA, CALLING_METHOD, ttl, undefined, true); + await cacheService.set(dataLabel, DATA, CALLING_METHOD, requestDetails, ttl); await new Promise((r) => setTimeout(r, 200)); - const cache = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const cache = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(cache).to.deep.eq(DATA, 'data is stored with TTL'); await new Promise((r) => setTimeout(r, ttl)); - const expiredCache = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const expiredCache = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(expiredCache).to.eq(null, 'cache expires after TTL period'); - const deletedCacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const deletedCacheFromService = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(deletedCacheFromService).to.eq(null, 'getAsync method cannot read expired cache'); }); @@ -85,10 +88,10 @@ describe('@cache-service Acceptance Tests for shared cache', function () { const serviceWithDisabledRedis = new CacheService(global.logger, registry); await new Promise((r) => setTimeout(r, 1000)); expect(serviceWithDisabledRedis.isRedisEnabled()).to.eq(false, 'redis is disabled'); - await serviceWithDisabledRedis.set(dataLabel, DATA, CALLING_METHOD, undefined, undefined, true); + await serviceWithDisabledRedis.set(dataLabel, DATA, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - const dataInLRU = await serviceWithDisabledRedis.getAsync(dataLabel, CALLING_METHOD); + const dataInLRU = await serviceWithDisabledRedis.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(dataInLRU).to.deep.eq(DATA, 'data is stored in local cache'); process.env.REDIS_ENABLED = 'true'; @@ -97,10 +100,10 @@ describe('@cache-service Acceptance Tests for shared cache', function () { it('Cache set by one instance can be accessed by another', async () => { const dataLabel = `${DATA_LABEL_PREFIX}4`; const otherServiceInstance = new CacheService(global.logger, registry); - await cacheService.set(dataLabel, DATA, CALLING_METHOD, undefined, undefined, true); + await cacheService.set(dataLabel, DATA, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - const cachedData = await otherServiceInstance.getAsync(dataLabel, CALLING_METHOD); + const cachedData = await otherServiceInstance.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(cachedData).to.deep.eq(DATA, 'cached data is read correctly by other service instance'); }); @@ -108,7 +111,7 @@ describe('@cache-service Acceptance Tests for shared cache', function () { const dataLabel = `${DATA_LABEL_PREFIX}_redis_error`; let currentRedisEnabledEnv; - let cacheService; + let cacheService: CacheService; before(async () => { currentRedisEnabledEnv = process.env.REDIS_ENABLED; @@ -126,10 +129,10 @@ describe('@cache-service Acceptance Tests for shared cache', function () { }); it('test getAsync operation', async () => { - await cacheService.set(dataLabel, DATA, CALLING_METHOD, undefined, undefined); + await cacheService.set(dataLabel, DATA, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - const dataInLRU = await cacheService.getAsync(dataLabel, CALLING_METHOD); + const dataInLRU = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(dataInLRU).to.deep.eq(DATA, 'data is stored in local cache'); }); @@ -140,21 +143,21 @@ describe('@cache-service Acceptance Tests for shared cache', function () { string: '5644', }; - await cacheService.multiSet(pairs, CALLING_METHOD, undefined, undefined); + await cacheService.multiSet(pairs, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); for (const key in pairs) { - const cachedValue = await cacheService.getAsync(key, CALLING_METHOD); + const cachedValue = await cacheService.getAsync(key, CALLING_METHOD, requestDetails); expect(cachedValue).deep.equal(pairs[key]); } }); it('test delete operation', async () => { - await cacheService.set(dataLabel, DATA, CALLING_METHOD, undefined, undefined); + await cacheService.set(dataLabel, DATA, CALLING_METHOD, requestDetails); await new Promise((r) => setTimeout(r, 200)); - await cacheService.delete(dataLabel, CALLING_METHOD); - const dataInLRU = await cacheService.getAsync(dataLabel, CALLING_METHOD); + await cacheService.delete(dataLabel, CALLING_METHOD, requestDetails); + const dataInLRU = await cacheService.getAsync(dataLabel, CALLING_METHOD, requestDetails); expect(dataInLRU).to.be.null; }); }); diff --git a/packages/server/tests/acceptance/equivalence.spec.ts b/packages/server/tests/acceptance/equivalence.spec.ts index 3991c0349c..5908f3946c 100644 --- a/packages/server/tests/acceptance/equivalence.spec.ts +++ b/packages/server/tests/acceptance/equivalence.spec.ts @@ -31,6 +31,7 @@ import pino from 'pino'; import { MirrorNodeClient } from '../../../relay/src/lib/clients'; import { hexToASCII } from '../../../relay/src/formatters'; import { AliasAccount } from '../types/AliasAccount'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; const logger = pino(); enum CallTypes { @@ -76,6 +77,7 @@ describe('Equivalence tests', async function () { const { servicesNode, mirrorNode, relay }: any = global; const servicesClient = servicesNode as ServicesClient; const mirrorNodeClient = mirrorNode as MirrorNodeClient; + const requestDetails = new RequestDetails({ requestId: 'rpc_batch1Test', ipAddress: '0.0.0.0' }); let precheck: Precheck; const SUCCESS = 'SUCCESS'; @@ -185,7 +187,10 @@ describe('Equivalence tests', async function () { equivalenceContractId = equivalenceContractReceipt.contractId.toString(); requestId = Utils.generateRequestId(); - const contractMirror = await mirrorNodeClient.get(`/contracts/${estimatePrecompileSolidityAddress}`, requestId); + const contractMirror = await mirrorNodeClient.get( + `/contracts/${estimatePrecompileSolidityAddress}`, + requestDetails, + ); accounts[0] = await servicesClient.createAccountWithContractIdKey( contractMirror.contract_id, @@ -355,7 +360,7 @@ describe('Equivalence tests', async function () { }; async function getResultByEntityIdAndTxTimestamp(entityId, txTimestamp) { - return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`); + return await mirrorNode.get(`/contracts/${entityId}/results/${txTimestamp}`, requestDetails); } /** @@ -364,7 +369,7 @@ describe('Equivalence tests', async function () { * @returns list of ContractActions */ async function getContractActions(transactionIdOrHash: string) { - return await mirrorNodeClient.get(`/contracts/results/${transactionIdOrHash}/actions`, Utils.generateRequestId()); + return await mirrorNode.get(`/contracts/results/${transactionIdOrHash}/actions`, requestDetails); } async function createFungibleToken() { diff --git a/packages/server/tests/acceptance/erc20.spec.ts b/packages/server/tests/acceptance/erc20.spec.ts index be6a043b8c..4cd322d9de 100644 --- a/packages/server/tests/acceptance/erc20.spec.ts +++ b/packages/server/tests/acceptance/erc20.spec.ts @@ -32,6 +32,8 @@ import { EthImpl } from '@hashgraph/json-rpc-relay/src/lib/eth'; // Constants from local resources import Constants from '../../../server/tests/helpers/constants'; +import ServicesClient from '../clients/servicesClient'; +import RelayClient from '../clients/relayClient'; chai.use(solidity); @@ -42,7 +44,9 @@ const extractRevertReason = (errorReason: string) => { describe('@erc20 Acceptance Tests', async function () { this.timeout(240 * 1000); // 240 seconds - const { servicesNode, relay }: any = global; + + // @ts-ignore + const { servicesNode, relay }: { servicesNode: ServicesClient; relay: RelayClient } = global; // cached entities const accounts: AliasAccount[] = []; @@ -285,7 +289,7 @@ describe('@erc20 Acceptance Tests', async function () { await contract .connect(tokenOwnerWallet) .approve(spender, initialSupply, await Utils.gasOptions(requestId)); - await contract.transfer(to, 1, await Utils.gasOptions(1_500_000)); + await contract.transfer(to, 1, await Utils.gasOptions(requestId)); // 5 seconds sleep to propagate the changes to mirror node await new Promise((r) => setTimeout(r, 5000)); }); @@ -347,7 +351,7 @@ describe('@erc20 Acceptance Tests', async function () { }); beforeEach('reducing balance', async function () { - await contract.transfer(to, 2, await Utils.gasOptions(1_500_000)); + await contract.transfer(to, 2, await Utils.gasOptions(requestId)); }); it('reverts', async function () { @@ -374,9 +378,7 @@ describe('@erc20 Acceptance Tests', async function () { amount = initialSupply; to = ethers.ZeroAddress; tokenOwnerWallet = accounts[2].wallet; - await contract - .connect(tokenOwnerWallet) - .approve(spender, amount, await Utils.gasOptions(requestId, 1_500_000)); + await contract.connect(tokenOwnerWallet).approve(spender, amount, await Utils.gasOptions(requestId)); }); it('reverts', async function () { diff --git a/packages/server/tests/acceptance/estimateGasContract.spec.ts b/packages/server/tests/acceptance/estimateGasContract.spec.ts index 848df84caa..20d38b7a5b 100644 --- a/packages/server/tests/acceptance/estimateGasContract.spec.ts +++ b/packages/server/tests/acceptance/estimateGasContract.spec.ts @@ -168,7 +168,7 @@ describe('EstimateGasContract tests', function () { const estimateGasResponse = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_ESTIMATE_GAS, [ { data: '0x0ec1551d', - to: Utils.add0xPrefix(contract.target), + to: Utils.add0xPrefix(contract.target as string), from: randomAddress, }, ]); diff --git a/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts b/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts index 508bfc3007..828743b179 100644 --- a/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts +++ b/packages/server/tests/acceptance/estimateGasPrecompile.spec.ts @@ -37,6 +37,9 @@ import RelayCalls from '../../../../packages/server/tests/helpers/constants'; // Other imports import { numberTo0x } from '../../../../packages/relay/src/formatters'; +import RelayClient from '../clients/relayClient'; +import ServicesClient from '../clients/servicesClient'; +import MirrorClient from '../clients/mirrorClient'; describe('EstimatePrecompileContract tests', function () { const signers: AliasAccount[] = []; @@ -72,7 +75,13 @@ describe('EstimatePrecompileContract tests', function () { const usdFee1 = 1; const usdFee2 = 2; const accounts: AliasAccount[] = []; - const { servicesNode, mirrorNode, relay }: any = global; + + // @ts-ignore + const { + servicesNode, + mirrorNode, + relay, + }: { servicesNode: ServicesClient; mirrorNode: MirrorClient; relay: RelayClient } = global; async function createFungibleToken() { estimateContract = new ethers.Contract( @@ -2369,7 +2378,7 @@ describe('EstimatePrecompileContract tests', function () { let estimateGasResponse; try { estimateGasResponse = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_ESTIMATE_GAS, [tx]); - } catch (e) { + } catch (e: any) { expect(e.code).to.eq(errorMessage); failed = true; } diff --git a/packages/server/tests/acceptance/hbarLimiter.spec.ts b/packages/server/tests/acceptance/hbarLimiter.spec.ts index 280c6b492c..357eb0a700 100644 --- a/packages/server/tests/acceptance/hbarLimiter.spec.ts +++ b/packages/server/tests/acceptance/hbarLimiter.spec.ts @@ -36,14 +36,37 @@ import parentContractJson from '../contracts/Parent.json'; import EstimateGasContract from '../contracts/EstimateGasContract.json'; import largeContractJson from '../contracts/hbarLimiterContracts/largeSizeContract.json'; import mediumSizeContract from '../contracts/hbarLimiterContracts/mediumSizeContract.json'; +import { resolve } from 'path'; +import { config } from 'dotenv'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; +import MirrorClient from '../clients/mirrorClient'; +import RelayClient from '../clients/relayClient'; +import { Logger } from 'pino'; +import MetricsClient from '../clients/metricsClient'; config({ path: resolve(__dirname, '../localAcceptance.env') }); describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { // @ts-ignore - const { mirrorNode, relay, logger, initialBalance, metrics, relayIsLocal } = global; + const { + mirrorNode, + relay, + logger, + initialBalance, + metrics, + relayIsLocal, + }: { + mirrorNode: MirrorClient; + relay: RelayClient; + logger: Logger; + initialBalance: string; + metrics: MetricsClient; + relayIsLocal: boolean; + } = global; const operatorAccount = process.env.OPERATOR_ID_MAIN; const fileAppendChunkSize = Number(process.env.FILE_APPEND_CHUNK_SIZE) || 5120; + const requestId = 'hbarLimiterTest'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); // The following tests exhaust the hbar limit, so they should only be run against a local relay if (relayIsLocal) { @@ -103,7 +126,7 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { return fileAppendTxFee; }; - const getExpectedCostOfLastLargeTx = async (requestId: string, txData: string) => { + const getExpectedCostOfLastLargeTx = async (txData: string, requestDetails: RequestDetails) => { const ethereumTransaction = ( await mirrorNode.get( `/transactions?transactiontype=ETHEREUMTRANSACTION&order=desc&account.id=${operatorAccount}&limit=1`, @@ -111,8 +134,8 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { ) ).transactions[0]; const ethereumTxFee = sumAccountTransfers(ethereumTransaction.transfers, operatorAccount); - const { fileCreateTxFee, fileCreateTimestamp } = await getExpectedCostOfFileCreateTx(requestId); - const fileAppendTxFee = await getExpectedCostOfFileAppendTx(requestId, fileCreateTimestamp, txData); + const { fileCreateTxFee, fileCreateTimestamp } = await getExpectedCostOfFileCreateTx(requestDetails); + const fileAppendTxFee = await getExpectedCostOfFileAppendTx(requestDetails, fileCreateTimestamp, txData); const fileDeleteTx = ( await mirrorNode.get( @@ -149,19 +172,14 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { type: 2, }; - // cached entities - let requestId: string; - let requestIdPrefix: string; - before(async function () { // Restart the relay to reset the limits await global.restartLocalRelay(); - requestId = Utils.generateRequestId(); - requestIdPrefix = Utils.formatRequestIdMessage(requestId); - - logger.info(`${requestIdPrefix} Creating accounts`); - logger.info(`${requestIdPrefix} HBAR_RATE_LIMIT_TINYBAR: ${process.env.HBAR_RATE_LIMIT_TINYBAR}`); + logger.info(`${requestDetails.formattedRequestId} Creating accounts`); + logger.info( + `${requestDetails.formattedRequestId} HBAR_RATE_LIMIT_TINYBAR: ${process.env.HBAR_RATE_LIMIT_TINYBAR}`, + ); const initialAccount: AliasAccount = global.accounts[0]; @@ -172,15 +190,13 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); }); beforeEach(async function () { - requestId = Utils.generateRequestId(); - requestIdPrefix = Utils.formatRequestIdMessage(requestId); await new Promise((r) => setTimeout(r, 3000)); }); @@ -192,7 +208,9 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { it('should execute "eth_sendRawTransaction" without triggering HBAR rate limit exceeded', async function () { const parentContract = await deployContract(parentContractJson, accounts[0].wallet); const parentContractAddress = parentContract.target as string; - global.logger.trace(`${requestIdPrefix} Deploy parent contract on address ${parentContractAddress}`); + global.logger.trace( + `${requestDetails.formattedRequestId} Deploy parent contract on address ${parentContractAddress}`, + ); const gasPrice = await relay.gasPrice(requestId); const remainingHbarsBefore = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT)); @@ -222,7 +240,10 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { const contract = await deployContract(largeContractJson, accounts[0].wallet); const remainingHbarsAfter = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT)); - const expectedCost = await getExpectedCostOfLastLargeTx(requestId, contract.deploymentTransaction()!.data); + const expectedCost = await getExpectedCostOfLastLargeTx( + contract.deploymentTransaction()!.data, + requestDetails, + ); verifyRemainingLimit(expectedCost, remainingHbarsBefore, remainingHbarsAfter); }); @@ -247,7 +268,10 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { const contract = await deployContract(mediumSizeContract, accounts[0].wallet); const remainingHbarsAfter = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT)); - const expectedCost = await getExpectedCostOfLastLargeTx(requestId, contract.deploymentTransaction()!.data); + const expectedCost = await getExpectedCostOfLastLargeTx( + contract.deploymentTransaction()!.data, + requestDetails, + ); verifyRemainingLimit(expectedCost, remainingHbarsBefore, remainingHbarsAfter); }); @@ -302,8 +326,8 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { const amountPaidByOperator = operatorBalanceBefore - operatorBalanceAfter; const totalOperatorFees = await getExpectedCostOfLastLargeTx( - requestId, largeContract.deploymentTransaction()!.data, + requestDetails, ); const remainingHbarsAfter = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT)); const hbarLimitReducedAmount = remainingHbarsBefore - remainingHbarsAfter; @@ -335,7 +359,7 @@ describe('@hbarlimiter HBAR Limiter Acceptance Tests', function () { process.env.HBAR_RATE_LIMIT_PREEMPTIVE_CHECK = 'false'; const remainingHbarsBefore = Number(await metrics.get(testConstants.METRICS.REMAINING_HBAR_LIMIT)); - let lastRemainingHbars = remainingHbarsBefore; + const lastRemainingHbars = remainingHbarsBefore; expect(remainingHbarsBefore).to.be.gt(0); try { for (let i = 0; i < 50; i++) { diff --git a/packages/server/tests/acceptance/htsPrecompile/precompileCalls.spec.ts b/packages/server/tests/acceptance/htsPrecompile/precompileCalls.spec.ts index 5e7cb79676..60e217b7a6 100644 --- a/packages/server/tests/acceptance/htsPrecompile/precompileCalls.spec.ts +++ b/packages/server/tests/acceptance/htsPrecompile/precompileCalls.spec.ts @@ -391,7 +391,7 @@ describe('@precompile-calls Tests for eth_call with HTS', async function () { expect(customFees.fixedFees).to.exist; expect(customFees.fixedFees.length).to.eq(1); expect(customFees.fixedFees[0].amount).to.exist; - expect(customFees.fixedFees[0].amount.toString()).to.eq(Hbar.from(1).toTinybars().toString()); + expect(customFees.fixedFees[0].amount.toString()).to.eq('1'); expect(customFees.fixedFees[0].tokenId).to.eq(ZERO_HEX); expect(customFees.fixedFees[0].feeCollector).to.exist; expect(customFees.fixedFees[0].feeCollector.toLowerCase()).to.eq(accounts[0].address.toLowerCase()); @@ -473,7 +473,7 @@ describe('@precompile-calls Tests for eth_call with HTS', async function () { expect(customFees.fixedFees.length).to.eq(2); expect(customFees.fixedFees[0].amount).to.exist; - expect(customFees.fixedFees[0].amount.toString()).to.eq(Hbar.from(1).toTinybars().toString()); + expect(customFees.fixedFees[0].amount.toString()).to.eq(Hbar.fromTinybars(1).toString()); expect(customFees.fixedFees[0].tokenId).to.eq(ZERO_HEX); expect(customFees.fixedFees[0].feeCollector).to.exist; expect(customFees.fixedFees[0].feeCollector.toLowerCase()).to.eq(accounts[0].address.toLowerCase()); diff --git a/packages/server/tests/acceptance/htsPrecompile_v1.spec.ts b/packages/server/tests/acceptance/htsPrecompile_v1.spec.ts index d5676af6d7..7a53850269 100644 --- a/packages/server/tests/acceptance/htsPrecompile_v1.spec.ts +++ b/packages/server/tests/acceptance/htsPrecompile_v1.spec.ts @@ -30,12 +30,24 @@ import { AliasAccount } from '../types/AliasAccount'; import { ethers } from 'ethers'; import BaseHTSJson from '../contracts/contracts_v1/BaseHTS.json'; import { Utils } from '../helpers/utils'; +import ServicesClient from '../clients/servicesClient'; +import RelayClient from '../clients/relayClient'; +import MirrorClient from '../clients/mirrorClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; chai.use(solidity); describe('@htsprecompilev1 HTS Precompile V1 Acceptance Tests', async function () { this.timeout(240 * 1000); // 240 seconds - const { servicesNode, relay, mirrorNode }: any = global; + + // @ts-ignore + const { + servicesNode, + relay, + mirrorNode, + }: { servicesNode: ServicesClient; relay: RelayClient; mirrorNode: MirrorClient } = global; + + const requestDetails = new RequestDetails({ requestId: 'htsPrecompile_v1Test', ipAddress: '0.0.0.0' }); const TX_SUCCESS_CODE = BigInt(22); @@ -48,36 +60,38 @@ describe('@htsprecompilev1 HTS Precompile V1 Acceptance Tests', async function ( let baseHTSContractReceiverWalletFirst; let baseHTSContractReceiverWalletSecond; let HTSTokenWithCustomFeesContractAddress; - let requestId; this.beforeAll(async () => { - requestId = Utils.generateRequestId(); - const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '5000000000'; //50 Hbar - const contractDeployer = await Utils.createAliasAccount(mirrorNode, initialAccount, requestId, initialAmount); + const contractDeployer = await Utils.createAliasAccount( + mirrorNode, + initialAccount, + requestDetails.requestId, + initialAmount, + ); const BaseHTSContract = await Utils.deployContract(BaseHTSJson.abi, BaseHTSJson.bytecode, contractDeployer.wallet); BaseHTSContractAddress = BaseHTSContract.target; - const contractMirror = await mirrorNode.get(`/contracts/${BaseHTSContractAddress}`, requestId); + const contractMirror = await mirrorNode.get(`/contracts/${BaseHTSContractAddress}`, requestDetails.requestId); accounts[0] = await servicesNode.createAccountWithContractIdKey( contractMirror.contract_id, 70, relay.provider, - requestId, + requestDetails.requestId, ); accounts[1] = await servicesNode.createAccountWithContractIdKey( contractMirror.contract_id, 25, relay.provider, - requestId, + requestDetails.requestId, ); accounts[2] = await servicesNode.createAccountWithContractIdKey( contractMirror.contract_id, 25, relay.provider, - requestId, + requestDetails.requestId, ); // allow mirror node a 2 full record stream write windows (2 sec) and a buffer to persist setup details @@ -90,10 +104,6 @@ describe('@htsprecompilev1 HTS Precompile V1 Acceptance Tests', async function ( baseHTSContractReceiverWalletSecond = baseHTSContract.connect(accounts[2].wallet); }); - this.beforeEach(async () => { - requestId = Utils.generateRequestId(); - }); - async function createHTSToken() { const baseHTSContract = new ethers.Contract(BaseHTSContractAddress, BaseHTSJson.abi, accounts[0].wallet); const tx = await baseHTSContract.createFungibleTokenPublic(accounts[0].wallet.address, { diff --git a/packages/server/tests/acceptance/index.spec.ts b/packages/server/tests/acceptance/index.spec.ts index 47468e8923..fa184a2ecd 100644 --- a/packages/server/tests/acceptance/index.spec.ts +++ b/packages/server/tests/acceptance/index.spec.ts @@ -48,6 +48,7 @@ import constants from '@hashgraph/json-rpc-relay/dist/lib/constants'; import { Utils } from '../helpers/utils'; import { AliasAccount } from '../types/AliasAccount'; import { setServerTimeout } from '../../src/koaJsonRpc/lib/utils'; +import { Server, IncomingMessage, ServerResponse } from 'http'; chai.use(chaiAsPromised); dotenv.config({ path: path.resolve(__dirname, '../../../.env') }); @@ -79,8 +80,6 @@ global.relayIsLocal = RELAY_URL === LOCAL_RELAY_URL; describe('RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds - let relayServer; // Relay Server - let socketServer; global.servicesNode = new ServicesClient( NETWORK, OPERATOR_ID, @@ -90,8 +89,6 @@ describe('RPC Server Acceptance Tests', function () { global.mirrorNode = new MirrorClient(MIRROR_NODE_URL, logger.child({ name: `mirror-node-test-client` })); global.metrics = new MetricsClient(RELAY_URL, logger.child({ name: `metrics-test-client` })); global.relay = new RelayClient(RELAY_URL, logger.child({ name: `relay-test-client` })); - global.relayServer = relayServer; - global.socketServer = socketServer; global.logger = logger; global.initialBalance = INITIAL_BALANCE; @@ -132,7 +129,7 @@ describe('RPC Server Acceptance Tests', function () { ); global.accounts = new Array(initialAccount); - await global.mirrorNode.get(`/accounts/${initialAccount.address}`); + await global.mirrorNode.get(`/accounts/${initialAccount.address}`, Utils.generateRequestId()); }); after(async function () { @@ -193,6 +190,8 @@ describe('RPC Server Acceptance Tests', function () { function stopRelay() { //stop relay logger.info('Stop relay'); + + const relayServer: Server = global.relayServer; if (relayServer !== undefined) { relayServer.close(); } @@ -206,7 +205,8 @@ describe('RPC Server Acceptance Tests', function () { // start local relay, relay instance in local should not be running logger.info(`Start relay on port ${constants.RELAY_PORT}`); - relayServer = app.listen({ port: constants.RELAY_PORT }); + const relayServer = app.listen({ port: constants.RELAY_PORT }); + global.relayServer = relayServer; setServerTimeout(relayServer); if (process.env.TEST_WS_SERVER === 'true') { diff --git a/packages/server/tests/acceptance/rateLimiter.spec.ts b/packages/server/tests/acceptance/rateLimiter.spec.ts index 62ff5521d0..a6ed98df71 100644 --- a/packages/server/tests/acceptance/rateLimiter.spec.ts +++ b/packages/server/tests/acceptance/rateLimiter.spec.ts @@ -22,12 +22,13 @@ import Assertions from '../helpers/assertions'; import testConstants from '../../tests/helpers/constants'; import relayConstants from '../../../../packages/relay/src/lib/constants'; +import RelayClient from '../clients/relayClient'; describe('@ratelimiter Rate Limiters Acceptance Tests', function () { this.timeout(480 * 1000); // 480 seconds // @ts-ignore - const { relay } = global; + const { relay }: { relay: RelayClient } = global; // cached entities let requestId: string; diff --git a/packages/server/tests/acceptance/rpc_batch1.spec.ts b/packages/server/tests/acceptance/rpc_batch1.spec.ts index 6680f884fd..833128fc0b 100644 --- a/packages/server/tests/acceptance/rpc_batch1.spec.ts +++ b/packages/server/tests/acceptance/rpc_batch1.spec.ts @@ -23,7 +23,7 @@ import { expect } from 'chai'; import { ethers } from 'ethers'; import { AliasAccount } from '../types/AliasAccount'; import { Utils } from '../helpers/utils'; -import { FileInfo, FileInfoQuery, TransferTransaction } from '@hashgraph/sdk'; +import { FileInfo, FileInfoQuery, Hbar, TransferTransaction } from '@hashgraph/sdk'; // Assertions from local resources import Assertions from '../helpers/assertions'; @@ -41,6 +41,11 @@ import RelayCalls from '../../tests/helpers/constants'; // Other imports import { numberTo0x, prepend0x } from '../../../../packages/relay/src/formatters'; import constants from '../../../relay/src/lib/constants'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; +import RelayClient from '../clients/relayClient'; +import ServicesClient from '../clients/servicesClient'; +import MirrorClient from '../clients/mirrorClient'; + const Address = RelayCalls; describe('@api-batch-1 RPC Server Acceptance Tests', function () { @@ -49,30 +54,40 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const accounts: AliasAccount[] = []; // @ts-ignore - const { servicesNode, mirrorNode, relay, initialBalance } = global; + const { + servicesNode, + mirrorNode, + relay, + initialBalance, + }: { servicesNode: ServicesClient; mirrorNode: MirrorClient; relay: RelayClient; initialBalance: string } = global; // cached entities let parentContractAddress: string; let mirrorContractDetails; - let requestId: string; let account2Address: string; let expectedGasPrice: string; + const requestId = 'rpc_batch1Test'; + const requestIdPrefix = Utils.formatRequestIdMessage(requestId); + const requestDetails = new RequestDetails({ requestId: 'rpc_batch1Test', ipAddress: '0.0.0.0' }); const CHAIN_ID = process.env.CHAIN_ID || '0x12a'; const INCORRECT_CHAIN_ID = 999; const GAS_PRICE_TOO_LOW = '0x1'; const GAS_PRICE_REF = '0x123456'; const ONE_TINYBAR = Utils.add0xPrefix(Utils.toHex(Constants.TINYBAR_TO_WEIBAR_COEF)); + const TEN_HBAR = Utils.add0xPrefix( + (BigInt(new Hbar(10).toTinybars().toString()) * BigInt(Constants.TINYBAR_TO_WEIBAR_COEF)).toString(16), + ); const sendRawTransaction = relay.sendRawTransaction; /** * resolves long zero addresses to EVM addresses by querying mirror node - * @param tx: any - supposedly a proper transaction that has `from` and `to` fields + * @param tx - supposedly a proper transaction that has `from` and `to` fields * @returns Promise<{from: any|null, to: any|null}> */ const resolveAccountEvmAddresses = async (tx: any) => { - const fromAccountInfo = await mirrorNode.get(`/accounts/${tx.from}`); - const toAccountInfo = await mirrorNode.get(`/accounts/${tx.to}`); + const fromAccountInfo = await mirrorNode.get(`/accounts/${tx.from}`, requestId); + const toAccountInfo = await mirrorNode.get(`/accounts/${tx.to}`, requestId); return { from: fromAccountInfo?.evm_address ?? tx.from, to: toAccountInfo?.evm_address ?? tx.to, @@ -83,9 +98,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds this.beforeAll(async () => { - requestId = Utils.generateRequestId(); - const requestIdPrefix = Utils.formatRequestIdMessage(requestId); - expectedGasPrice = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GAS_PRICE, [], requestId); + expectedGasPrice = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GAS_PRICE, [], requestIdPrefix); const initialAccount: AliasAccount = global.accounts[0]; const neededAccounts: number = 3; @@ -95,7 +108,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -106,7 +119,9 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { accounts[0].wallet, ); parentContractAddress = parentContract.target as string; - global.logger.trace(`${requestIdPrefix} Deploy parent contract on address ${parentContractAddress}`); + global.logger.trace( + `${requestDetails.formattedRequestId} Deploy parent contract on address ${parentContractAddress}`, + ); await accounts[0].wallet.sendTransaction({ to: parentContractAddress, @@ -117,7 +132,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const createChildTx: ethers.ContractTransactionResponse = await parentContract.createChild(1); global.logger.trace( - `${requestIdPrefix} Contract call createChild on parentContract results in tx hash: ${createChildTx.hash}`, + `${requestDetails.formattedRequestId} Contract call createChild on parentContract results in tx hash: ${createChildTx.hash}`, ); // get contract result details mirrorContractDetails = await mirrorNode.get(`/contracts/results/${createChildTx.hash}`, requestId); @@ -126,10 +141,6 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { account2Address = accounts[2].address; }); - this.beforeEach(async () => { - requestId = Utils.generateRequestId(); - }); - describe('eth_getLogs', () => { let log0Block, log4Block, contractAddress: string, contractAddress2: string, latestBlock, previousBlock; @@ -147,7 +158,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { contractAddress = logsContract.target.toString(); contractAddress2 = logsContract2.target.toString(); - previousBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestId)); + previousBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestIdPrefix)); // @ts-ignore await (await logsContract.connect(accounts[1].wallet).log0(1)).wait(); @@ -162,7 +173,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { // @ts-ignore await (await logsContract2.connect(accounts[1].wallet).log4(1, 1, 1, 1)).wait(); - latestBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestId)); + latestBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestIdPrefix)); }); it('@release should deploy a contract', async () => { @@ -175,7 +186,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -195,7 +206,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { log0Block = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [logs[0].transactionHash], - requestId, + requestIdPrefix, ); const transactionCountLog0Block = await relay.provider.getTransactionCount( log0Block.from, @@ -205,7 +216,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { log4Block = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [logs[logs.length - 1].transactionHash], - requestId, + requestIdPrefix, ); const transactionCountLog4Block = await relay.provider.getTransactionCount( log4Block.from, @@ -234,7 +245,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -253,7 +264,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { }, ], predefined.MISSING_FROM_BLOCK_PARAM, - requestId, + requestIdPrefix, ); }); @@ -267,7 +278,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -289,7 +300,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: contractAddress, }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -311,7 +322,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: contractAddress, }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -332,7 +343,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2, Address.NON_EXISTING_ADDRESS], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); expect(logs.length).to.be.eq(6); @@ -353,7 +364,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(0); @@ -371,20 +382,24 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs).to.exist; expect(logs.length).to.be.eq(0); }); it('should be able to use `topics` param', async () => { - const logs = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_LOGS, [ - { - fromBlock: log0Block.blockNumber, - toBlock: log4Block.blockNumber, - address: [contractAddress, contractAddress2], - }, - ]); + const logs = await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_LOGS, + [ + { + fromBlock: log0Block.blockNumber, + toBlock: log4Block.blockNumber, + address: [contractAddress, contractAddress2], + }, + ], + requestIdPrefix, + ); expect(logs.length).to.be.greaterThan(0); //using second log in array, because the first doesn't contain any topics const topic = logs[1].topics[0]; @@ -398,7 +413,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { topics: [topic], }, ], - requestId, + requestIdPrefix, ); expect(logsWithTopic.length).to.be.greaterThan(0); @@ -414,7 +429,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { process.env['MIRROR_NODE_LIMIT_PARAM'] = '2'; // calculate blocks behind latest, so we can fetch logs from the past. // if current block is less than 10, we will fetch logs from the beginning otherwise we will fetch logs from 10 blocks behind latest - const currentBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestId)); + const currentBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestIdPrefix)); let blocksBehindLatest = 0; if (currentBlock > 10) { blocksBehindLatest = currentBlock - 10; @@ -428,7 +443,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [contractAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.be.greaterThan(2); }); @@ -443,13 +458,13 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: ethers.ZeroAddress, }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.eq(0); }); it('should return only logs of non-zero addresses', async () => { - const currentBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestId)); + const currentBlock = Number(await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestIdPrefix)); let blocksBehindLatest = 0; if (currentBlock > 10) { blocksBehindLatest = currentBlock - 10; @@ -463,7 +478,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { address: [ethers.ZeroAddress, contractAddress2], }, ], - requestId, + requestIdPrefix, ); expect(logs.length).to.eq(1); }); @@ -499,7 +514,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_HASH, [mirrorBlock.hash.substring(0, 66), false], - requestId, + requestIdPrefix, ); Assertions.block(blockResult, mirrorBlock, mirrorTransactions, expectedGasPrice, false); }); @@ -508,7 +523,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_HASH, [mirrorBlock.hash.substring(0, 66), true], - requestId, + requestIdPrefix, ); // Remove synthetic transactions blockResult.transactions = blockResult.transactions.filter((transaction) => transaction.value !== '0x1234'); @@ -519,7 +534,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_HASH, [Address.NON_EXISTING_BLOCK_HASH, false], - requestId, + requestIdPrefix, ); expect(blockResult).to.be.null; }); @@ -528,7 +543,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_HASH, [Address.NON_EXISTING_BLOCK_HASH, true], - requestId, + requestIdPrefix, ); expect(blockResult).to.be.null; }); @@ -537,7 +552,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [numberTo0x(mirrorBlock.number), false], - requestId, + requestIdPrefix, ); // Remove synthetic transactions blockResult.transactions = blockResult.transactions.filter((transaction) => transaction.value !== '0x1234'); @@ -548,14 +563,14 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['latest', false], - requestId, + requestIdPrefix, ); await Utils.wait(1000); const blockResult2 = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['latest', false], - requestId, + requestIdPrefix, ); expect(blockResult).to.not.deep.equal(blockResult2); }); @@ -564,14 +579,14 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['finalized', false], - requestId, + requestIdPrefix, ); await Utils.wait(1000); const blockResult2 = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['finalized', false], - requestId, + requestIdPrefix, ); expect(blockResult).to.not.deep.equal(blockResult2); }); @@ -580,14 +595,14 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['safe', false], - requestId, + requestIdPrefix, ); await Utils.wait(1000); const blockResult2 = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['safe', false], - requestId, + requestIdPrefix, ); expect(blockResult).to.not.deep.equal(blockResult2); }); @@ -596,14 +611,14 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['pending', false], - requestId, + requestIdPrefix, ); await Utils.wait(1000); const blockResult2 = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, ['pending', false], - requestId, + requestIdPrefix, ); expect(blockResult).to.not.deep.equal(blockResult2); }); @@ -612,7 +627,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [numberTo0x(mirrorBlock.number), true], - requestId, + requestIdPrefix, ); // Remove synthetic transactions blockResult.transactions = blockResult.transactions.filter((transaction) => transaction.value !== '0x1234'); @@ -623,7 +638,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [Address.NON_EXISTING_BLOCK_NUMBER, true], - requestId, + requestIdPrefix, ); expect(blockResult).to.be.null; }); @@ -632,7 +647,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const blockResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [Address.NON_EXISTING_BLOCK_NUMBER, false], - requestId, + requestIdPrefix, ); expect(blockResult).to.be.null; }); @@ -641,7 +656,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_NUMBER, [numberTo0x(mirrorBlock.number)], - requestId, + requestIdPrefix, ); expect(res).to.be.equal(ethers.toQuantity(mirrorBlock.count)); }); @@ -650,7 +665,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_NUMBER, [Address.NON_EXISTING_BLOCK_NUMBER], - requestId, + requestIdPrefix, ); expect(res).to.be.null; }); @@ -659,7 +674,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_HASH, [mirrorBlock.hash.substring(0, 66)], - requestId, + requestIdPrefix, ); expect(res).to.be.equal(ethers.toQuantity(mirrorBlock.count)); }); @@ -668,7 +683,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_TRANSACTION_COUNT_BY_HASH, [Address.NON_EXISTING_BLOCK_HASH], - requestId, + requestIdPrefix, ); expect(res).to.be.null; }); @@ -680,7 +695,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { expect(mirrorBlocks.blocks.length).to.gt(0); const mirrorBlockNumber = mirrorBlocks.blocks[0].number; - const res = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestId); + const res = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_BLOCK_NUMBER, [], requestIdPrefix); const blockNumber = Number(res); expect(blockNumber).to.exist; @@ -728,7 +743,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_HASH_AND_INDEX, [mirrorContractDetails.block_hash.substring(0, 66), numberTo0x(mirrorContractDetails.transaction_index)], - requestId, + requestIdPrefix, ); Assertions.transaction(response, mirrorContractDetails); }); @@ -737,7 +752,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_HASH_AND_INDEX, [Address.NON_EXISTING_BLOCK_HASH, numberTo0x(mirrorContractDetails.transaction_index)], - requestId, + requestIdPrefix, ); expect(response).to.be.null; }); @@ -746,7 +761,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_HASH_AND_INDEX, [mirrorContractDetails.block_hash.substring(0, 66), Address.NON_EXISTING_INDEX], - requestId, + requestIdPrefix, ); expect(response).to.be.null; }); @@ -755,7 +770,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_NUMBER_AND_INDEX, [numberTo0x(mirrorContractDetails.block_number), numberTo0x(mirrorContractDetails.transaction_index)], - requestId, + requestIdPrefix, ); Assertions.transaction(response, mirrorContractDetails); }); @@ -764,7 +779,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_NUMBER_AND_INDEX, [numberTo0x(mirrorContractDetails.block_number), Address.NON_EXISTING_INDEX], - requestId, + requestIdPrefix, ); expect(response).to.be.null; }); @@ -773,7 +788,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const response = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_BLOCK_NUMBER_AND_INDEX, [Address.NON_EXISTING_BLOCK_NUMBER, numberTo0x(mirrorContractDetails.transaction_index)], - requestId, + requestIdPrefix, ); expect(response).to.be.null; }); @@ -795,14 +810,18 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { mirrorResult.from = accounts[2].wallet.address; mirrorResult.to = parentContractAddress; - const res = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [legacyTxHash], requestId); + const res = await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, + [legacyTxHash], + requestIdPrefix, + ); const currentPrice = await relay.gasPrice(requestId); Assertions.transactionReceipt(res, mirrorResult, currentPrice); }); it('@release-light, @release should execute "eth_getTransactionReceipt" for hash of London transaction', async function () { - const gasPrice = await relay.gasPrice(requestId); + const gasPrice = await relay.gasPrice(requestDetails); const transaction = { ...defaultLondonTransactionData, to: parentContractAddress, @@ -822,7 +841,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [transactionHash], - requestId, + requestIdPrefix, ); const currentPrice = await relay.gasPrice(requestId); @@ -848,7 +867,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [transactionHash], - requestId, + requestIdPrefix, ); const currentPrice = await relay.gasPrice(requestId); @@ -870,7 +889,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ signedTx + '11', - requestId, + requestDetails, ]); }); @@ -895,11 +914,15 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { } // load the block in cache - await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [formattedBlockNumber, true], requestId); + await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, + [formattedBlockNumber, true], + requestIdPrefix, + ); const receiptFromRelay = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [transactionHash], - requestId, + requestIdPrefix, ); // handle deviation in gas price @@ -913,11 +936,15 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const formattedBlockNumber = prepend0x(mirrorContractDetails.block_number.toString(16)); // load the block in cache - await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, [formattedBlockNumber, true], requestId); + await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_BLOCK_BY_NUMBER, + [formattedBlockNumber, true], + requestIdPrefix, + ); const receiptFromRelay = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [transactionHash], - requestId, + requestIdPrefix, ); // handle deviation in gas price @@ -929,7 +956,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [Address.NON_EXISTING_TX_HASH], - requestId, + requestIdPrefix, ); expect(res).to.be.null; }); @@ -944,14 +971,14 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.UNSUPPORTED_CHAIN_ID(ethers.toQuantity(INCORRECT_CHAIN_ID), CHAIN_ID); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should execute "eth_sendRawTransaction" for deterministic deployment transaction', async function () { // send gas money to the proxy deployer const sendHbarTx = { ...defaultLegacyTransactionData, - value: (10 * ONE_TINYBAR * 10 ** 8).toString(), // 10hbar - the gasPrice to deploy the deterministic proxy contract + value: TEN_HBAR, // 10hbar - the gasPrice to deploy the deterministic proxy contract to: constants.DETERMINISTIC_DEPLOYMENT_SIGNER, nonce: await relay.getAccountNonce(accounts[0].address, requestId), gasPrice: await relay.gasPrice(requestId), @@ -983,8 +1010,8 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { ); const receipt = await mirrorNode.get(`/contracts/results/${deterministicDeployTransactionHash}`, requestId); - const fromAccountInfo = await global.mirrorNode.get(`/accounts/${receipt.from}`); - const toAccountInfo = await global.mirrorNode.get(`/accounts/${receipt.to}`); + const fromAccountInfo = await global.mirrorNode.get(`/accounts/${receipt.from}`, requestId); + const toAccountInfo = await global.mirrorNode.get(`/accounts/${receipt.to}`, requestId); expect(receipt).to.exist; expect(fromAccountInfo.evm_address).to.eq(constants.DETERMINISTIC_DEPLOYMENT_SIGNER); @@ -994,7 +1021,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { try { await relay.sendRawTransaction(constants.DETERMINISTIC_DEPLOYER_TRANSACTION, requestId); expect(true).to.be.false; - } catch (error) { + } catch (error: any) { const expectedNonceTooLowError = predefined.NONCE_TOO_LOW(0, signerNonce); const errObj = JSON.parse(error.info.responseBody).error; expect(errObj.code).to.eq(expectedNonceTooLowError.code); @@ -1004,7 +1031,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { }); it('@release-light, @release should execute "eth_sendRawTransaction" for legacy EIP 155 transactions', async function () { - const receiverInitialBalance = await relay.getBalance(parentContractAddress, 'latest', requestId); + const receiverInitialBalance = await relay.getBalance(parentContractAddress, 'latest', requestDetails); const transaction = { ...default155TransactionData, to: parentContractAddress, @@ -1035,7 +1062,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.INSUFFICIENT_ACCOUNT_BALANCE; - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should execute "eth_sendRawTransaction" for legacy transactions (with no chainId i.e. chainId=0x0)', async function () { @@ -1071,7 +1098,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const transactionResult = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [transactionHash], - requestId, + requestIdPrefix, ); const result = Object.prototype.hasOwnProperty.call(transactionResult, 'chainId'); @@ -1089,7 +1116,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_PRICE_TOO_LOW(GAS_PRICE_TOO_LOW, GAS_PRICE_REF); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]); }); it('should not fail "eth_sendRawTransactxion" for Legacy 2930 transactions', async function () { @@ -1116,7 +1143,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_PRICE_TOO_LOW(GAS_PRICE_TOO_LOW, GAS_PRICE_REF); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]); }); it('should fail "eth_sendRawTransaction" for Legacy 2930 transactions (with insufficient balance)', async function () { @@ -1131,7 +1158,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.INSUFFICIENT_ACCOUNT_BALANCE; - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should fail "eth_sendRawTransaction" for London transactions (with gas price too low)', async function () { @@ -1145,7 +1172,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_PRICE_TOO_LOW(GAS_PRICE_TOO_LOW, GAS_PRICE_REF); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]); }); it('should fail "eth_sendRawTransaction" for London transactions (with insufficient balance)', async function () { @@ -1163,7 +1190,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.INSUFFICIENT_ACCOUNT_BALANCE; - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should execute "eth_sendRawTransaction" for London transactions', async function () { @@ -1258,7 +1285,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { await Utils.wait(1000); const txInfo = await mirrorNode.get(`/contracts/results/${transactionHash}`, requestId); - const contractResult = await mirrorNode.get(`/contracts/${txInfo.contract_id}`); + const contractResult = await mirrorNode.get(`/contracts/${txInfo.contract_id}`, requestId); const fileInfo = await new FileInfoQuery().setFileId(contractResult.file_id).execute(servicesNode.client); expect(fileInfo).to.exist; expect(fileInfo instanceof FileInfo).to.be.true; @@ -1281,7 +1308,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[1].wallet.signTransaction(transaction); const error = predefined.TRANSACTION_SIZE_TOO_BIG('132320', String(constants.SEND_RAW_TRANSACTION_SIZE_LIMIT)); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should execute "eth_sendRawTransaction" of type 1 and deploy a real contract', async function () { @@ -1372,7 +1399,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.INTERNAL_ERROR(); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestDetails]); }); describe('Prechecks', async function () { @@ -1386,7 +1413,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.UNSUPPORTED_CHAIN_ID('0x3e7', CHAIN_ID); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('should fail "eth_sendRawTransaction" for EIP155 transaction with not enough gas', async function () { @@ -1402,7 +1429,10 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_LIMIT_TOO_LOW(gasLimit, Constants.MAX_GAS_PER_SEC); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ + signedTx, + requestDetails, + ]); }); it('should fail "eth_sendRawTransaction" for EIP155 transaction with a too high gasLimit', async function () { @@ -1418,7 +1448,10 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_LIMIT_TOO_HIGH(gasLimit, Constants.MAX_GAS_PER_SEC); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ + signedTx, + requestDetails, + ]); }); it('should fail "eth_sendRawTransaction" for London transaction with not enough gas', async function () { @@ -1432,7 +1465,10 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_LIMIT_TOO_LOW(gasLimit, Constants.MAX_GAS_PER_SEC); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ + signedTx, + requestDetails, + ]); }); it('should fail "eth_sendRawTransaction" for London transaction with a too high gasLimit', async function () { @@ -1446,7 +1482,10 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_LIMIT_TOO_HIGH(gasLimit, Constants.MAX_GAS_PER_SEC); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ + signedTx, + requestDetails, + ]); }); it('should fail "eth_sendRawTransaction" for legacy EIP 155 transactions (with gas price too low)', async function () { @@ -1459,7 +1498,10 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.GAS_PRICE_TOO_LOW(GAS_PRICE_TOO_LOW, GAS_PRICE_REF); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, false, relay, [ + signedTx, + requestDetails, + ]); }); it('@release fail "eth_getTransactionReceipt" on precheck with wrong nonce error when sending a tx with the same nonce twice', async function () { @@ -1477,12 +1519,16 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { mirrorResult.from = accounts[2].wallet.address; mirrorResult.to = parentContractAddress; - const res = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [txHash1], requestId); + const res = await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, + [txHash1], + requestIdPrefix, + ); const currentPrice = await relay.gasPrice(requestId); Assertions.transactionReceipt(res, mirrorResult, currentPrice); const error = predefined.NONCE_TOO_LOW(nonce, nonce + 1); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('@release fail "eth_getTransactionReceipt" on precheck with wrong nonce error when sending a tx with a higher nonce', async function () { @@ -1498,7 +1544,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const signedTx = await accounts[2].wallet.signTransaction(transaction); const error = predefined.NONCE_TOO_HIGH(nonce + 100, nonce); - await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestId]); + await Assertions.assertPredefinedRpcError(error, sendRawTransaction, true, relay, [signedTx, requestDetails]); }); it('@release fail "eth_getTransactionReceipt" on submitting with wrong nonce error when sending a tx with the same nonce twice', async function () { @@ -1552,7 +1598,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [transactionHash], - requestId, + requestIdPrefix, ); const addressResult = await mirrorNode.get(`/accounts/${res.from}`, requestId); mirrorTransaction.from = addressResult.evm_address; @@ -1564,7 +1610,7 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () { const res = await relay.call( RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [Address.NON_EXISTING_TX_HASH], - requestId, + requestIdPrefix, ); expect(res).to.be.null; }); diff --git a/packages/server/tests/acceptance/rpc_batch2.spec.ts b/packages/server/tests/acceptance/rpc_batch2.spec.ts index cb66a2e1bb..6024db73c0 100644 --- a/packages/server/tests/acceptance/rpc_batch2.spec.ts +++ b/packages/server/tests/acceptance/rpc_batch2.spec.ts @@ -43,6 +43,10 @@ import RelayCalls from '../../tests/helpers/constants'; import Helper from '../../tests/helpers/constants'; import Address from '../../tests/helpers/constants'; import constants from '../../tests/helpers/constants'; +import RelayClient from '../clients/relayClient'; +import ServicesClient from '../clients/servicesClient'; +import MirrorClient from '../clients/mirrorClient'; +import { Logger } from 'pino'; describe('@api-batch-2 RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds @@ -50,7 +54,19 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { const accounts: AliasAccount[] = []; // @ts-ignore - const { servicesNode, mirrorNode, relay, logger, initialBalance } = global; + const { + servicesNode, + mirrorNode, + relay, + logger, + initialBalance, + }: { + servicesNode: ServicesClient; + mirrorNode: MirrorClient; + relay: RelayClient; + logger: Logger; + initialBalance: string; + } = global; // cached entities let tokenId; @@ -84,14 +100,18 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { const signedTx = await accounts.wallet.signTransaction(transaction); const txHash = await relay.sendRawTransaction(signedTx, requestId); await mirrorNode.get(`/contracts/results/${txHash}`, requestId); - await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [txHash]); + await relay.call( + RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, + [txHash], + Utils.formatRequestIdMessage(requestId), + ); await new Promise((r) => setTimeout(r, 2000)); }; this.beforeAll(async () => { requestId = Utils.generateRequestId(); const requestIdPrefix = Utils.formatRequestIdMessage(requestId); - expectedGasPrice = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GAS_PRICE, [], requestId); + expectedGasPrice = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GAS_PRICE, [], requestIdPrefix); const initialAccount: AliasAccount = global.accounts[0]; @@ -546,7 +566,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { requestId, ); const acc3Nonce = await relay.getAccountNonce(accounts[3].address); - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: ONE_TINYBAR, @@ -726,7 +746,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { async function createNftHTSToken(account) { const mainContract = new ethers.Contract(mainContractAddress, TokenCreateJson.abi, accounts[0].wallet); const tx = await mainContract.createNonFungibleTokenPublic(account.wallet.address, { - value: 30000000000000000000n, + value: BigInt('30000000000000000000'), ...Helper.GAS.LIMIT_5_000_000, }); const { tokenAddress } = (await tx.wait()).logs.filter( @@ -831,7 +851,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { ); expect(beginStorageVal).to.eq(BEGIN_EXPECTED_STORAGE_VAL); - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: 0, gasLimit: 50000, @@ -868,7 +888,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { requestId, ); - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: 0, gasLimit: 50000, @@ -925,7 +945,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { ); expect(beginStorageVal).to.eq(BEGIN_EXPECTED_STORAGE_VAL); - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: 0, gasLimit: 50000, @@ -956,7 +976,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { it('should execute "eth_getStorageAt" request to get current state changes with passing specific block', async function () { const EXPECTED_STORAGE_VAL = '0x0000000000000000000000000000000000000000000000000000000000000008'; - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: 0, gasLimit: 50000, @@ -1002,7 +1022,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { it('should execute "eth_getStorageAt" request to get current state changes with passing specific block hash', async function () { const EXPECTED_STORAGE_VAL = '0x0000000000000000000000000000000000000000000000000000000000000008'; - const gasPrice = await relay.gasPrice(); + const gasPrice = await relay.gasPrice(requestId); const transaction = { value: 0, gasLimit: 50000, @@ -1157,7 +1177,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { const getTxData = async (hash) => { const txByHash = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_BY_HASH, [hash], requestId); const receipt = await relay.call(RelayCalls.ETH_ENDPOINTS.ETH_GET_TRANSACTION_RECEIPT, [hash], requestId); - let mirrorResult = await mirrorNode.get(`/contracts/results/${hash}`, requestId); + const mirrorResult = await mirrorNode.get(`/contracts/results/${hash}`, requestId); return { txByHash, receipt, mirrorResult }; }; @@ -1170,7 +1190,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { await tx.wait(); - let { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); + const { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); mirrorResult.from = accounts[0].wallet.address; mirrorResult.to = accounts[1].wallet.address; @@ -1195,7 +1215,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { await tx.wait(); - let { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); + const { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); mirrorResult.from = accounts[0].wallet.address; mirrorResult.to = relayContract.target; @@ -1219,7 +1239,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { await tx.wait(); - let { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); + const { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); mirrorResult.from = accounts[0].wallet.address; mirrorResult.to = parentContractLongZeroAddress; @@ -1240,7 +1260,7 @@ describe('@api-batch-2 RPC Server Acceptance Tests', function () { await tx.wait(); - let { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); + const { txByHash, receipt, mirrorResult } = await getTxData(tx.hash); mirrorResult.from = accounts[0].wallet.address; diff --git a/packages/server/tests/acceptance/rpc_batch3.spec.ts b/packages/server/tests/acceptance/rpc_batch3.spec.ts index c5de6a340a..5bff80a411 100644 --- a/packages/server/tests/acceptance/rpc_batch3.spec.ts +++ b/packages/server/tests/acceptance/rpc_batch3.spec.ts @@ -46,12 +46,15 @@ import DeployerContractJson from '../contracts/Deployer.json'; import HederaTokenServiceImplJson from '../contracts/HederaTokenServiceImpl.json'; import EstimateGasContract from '../contracts/EstimateGasContract.json'; import HRC719ContractJson from '../contracts/HRC719Contract.json'; -import TokenCreateJson from '../contracts/TokenCreateContract.json'; // Helper functions/constants from local resources import { EthImpl } from '@hashgraph/json-rpc-relay/src/lib/eth'; import { predefined } from '@hashgraph/json-rpc-relay'; import { TYPES } from '../../src/validator'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; +import RelayClient from '../clients/relayClient'; +import ServicesClient from '../clients/servicesClient'; +import MirrorClient from '../clients/mirrorClient'; chai.use(chaiExclude); @@ -59,9 +62,14 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds const accounts: AliasAccount[] = []; + const requestDetails = new RequestDetails({ requestId: 'rpc_batch1Test', ipAddress: '0.0.0.0' }); // @ts-ignore - const { servicesNode, mirrorNode, relay } = global; + const { + servicesNode, + mirrorNode, + relay, + }: { servicesNode: ServicesClient; mirrorNode: MirrorClient; relay: RelayClient } = global; let mirrorPrimaryAccount: ethers.Wallet; let mirrorSecondaryAccount: ethers.Wallet; @@ -110,7 +118,7 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -757,7 +765,7 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () { adminPrivateKey: accounts[1].privateKey, }); - tokenAddress = Utils.idToEvmAddress(htsResult.receipt.tokenId.toString()); + tokenAddress = Utils.idToEvmAddress(htsResult.receipt.tokenId!.toString()); // Deploy a contract implementing HederaTokenService const HederaTokenServiceImplFactory = new ethers.ContractFactory( @@ -1245,7 +1253,7 @@ describe('@api-batch-3 RPC Server Acceptance Tests', function () { const signedTransaction = await accounts[0].wallet.signTransaction(transaction); const transactionHash = await relay.sendRawTransaction(signedTransaction, requestId); - estimateGasContractAddress = await mirrorNode.get(`/contracts/results/${transactionHash}`); + estimateGasContractAddress = await mirrorNode.get(`/contracts/results/${transactionHash}`, requestId); }); describe('Positive scenarios', async function () { diff --git a/packages/server/tests/acceptance/serverConfig.spec.ts b/packages/server/tests/acceptance/serverConfig.spec.ts index e46d2e0ae1..3edc034b42 100644 --- a/packages/server/tests/acceptance/serverConfig.spec.ts +++ b/packages/server/tests/acceptance/serverConfig.spec.ts @@ -32,7 +32,7 @@ describe('@server-config Server Configuration Options Coverage', function () { try { await Utils.sendJsonRpcRequestWithDelay(host, port, method, params, requestTimeoutMs + 1000); throw new Error('Request did not timeout as expected'); // Force the test to fail if the request does not time out - } catch (err) { + } catch (err: any) { expect(err.code).to.equal('ECONNRESET'); expect(err.message).to.equal('socket hang up'); } diff --git a/packages/server/tests/clients/mirrorClient.ts b/packages/server/tests/clients/mirrorClient.ts index 51565a03a9..f0aebccd2d 100644 --- a/packages/server/tests/clients/mirrorClient.ts +++ b/packages/server/tests/clients/mirrorClient.ts @@ -21,6 +21,7 @@ import Axios, { AxiosInstance } from 'axios'; import axiosRetry from 'axios-retry'; import { Logger } from 'pino'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; import { Utils } from '../helpers/utils'; export default class MirrorClient { diff --git a/packages/server/tests/clients/relayClient.ts b/packages/server/tests/clients/relayClient.ts index 1427a6a64c..7b3bbbb6fe 100644 --- a/packages/server/tests/clients/relayClient.ts +++ b/packages/server/tests/clients/relayClient.ts @@ -18,14 +18,14 @@ * */ -import { ethers } from 'ethers'; +import { BlockTag, ethers } from 'ethers'; import { Logger } from 'pino'; import Assertions from '../helpers/assertions'; -import { predefined } from '../../../relay/src/lib/errors/JsonRpcError'; +import { predefined } from '@hashgraph/json-rpc-relay'; import { Utils } from '../helpers/utils'; export default class RelayClient { - private readonly provider: ethers.JsonRpcProvider; + readonly provider: ethers.JsonRpcProvider; private readonly logger: Logger; constructor(relayUrl: string, logger: Logger) { @@ -42,7 +42,6 @@ export default class RelayClient { */ async call(methodName: string, params: any[], requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); - const result = await this.provider.send(methodName, params); this.logger.trace( `${requestIdPrefix} [POST] to relay '${methodName}' with params [${JSON.stringify( @@ -61,7 +60,7 @@ export default class RelayClient { * @param payload * @returns */ - async callBatch(payload: { id: string; method: string; params: any[] }[]) { + async callBatch(payload: { id: number; method: string; params: any[] }[]) { const request = this.provider._getConnection(); request.setHeader('content-type', 'application/json'); request.body = JSON.stringify(payload.map((r) => ({ ...r, jsonrpc: '2.0' }))); @@ -75,6 +74,7 @@ export default class RelayClient { * Calls the specified methodName with the provided params and asserts that it fails * @param methodName * @param params + * @param expectedRpcError * @param requestId */ async callFailing( @@ -126,7 +126,7 @@ export default class RelayClient { * @param block * @param requestId */ - async getBalance(address, block = 'latest', requestId?: string) { + async getBalance(address: ethers.AddressLike, block: BlockTag = 'latest', requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); this.logger.debug(`${requestIdPrefix} [POST] to relay eth_getBalance for address ${address}]`); return this.provider.getBalance(address, block); @@ -137,7 +137,7 @@ export default class RelayClient { * @param requestId * Returns: The nonce of the account with the provided `evmAddress` */ - async getAccountNonce(evmAddress, requestId?: string): Promise { + async getAccountNonce(evmAddress: string, requestId?: string): Promise { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); this.logger.debug(`${requestIdPrefix} [POST] to relay for eth_getTransactionCount for address ${evmAddress}`); const nonce = await this.provider.send('eth_getTransactionCount', [evmAddress, 'latest']); diff --git a/packages/server/tests/clients/servicesClient.ts b/packages/server/tests/clients/servicesClient.ts index 6d56d983fa..88767f2648 100644 --- a/packages/server/tests/clients/servicesClient.ts +++ b/packages/server/tests/clients/servicesClient.ts @@ -24,13 +24,10 @@ import { AccountId, AccountInfoQuery, Client, - ContractCreateTransaction, ContractExecuteTransaction, ContractFunctionParameters, ContractId, - FileCreateTransaction, Hbar, - Key, KeyList, PrivateKey, Query, @@ -43,7 +40,6 @@ import { FileUpdateTransaction, TransactionId, AccountAllowanceApproveTransaction, - AccountBalance, FileContentsQuery, TokenType, TokenSupplyType, @@ -53,19 +49,46 @@ import { CustomFractionalFee, CustomRoyaltyFee, EvmAddress, + CustomFee, + TokenId, + Key, } from '@hashgraph/sdk'; import { Logger } from 'pino'; -import { ethers } from 'ethers'; +import { ethers, JsonRpcProvider } from 'ethers'; import { Utils } from '../helpers/utils'; import { AliasAccount } from '../types/AliasAccount'; import { Utils as relayUtils } from '@hashgraph/json-rpc-relay/dist/utils'; +import { Long } from 'long'; const supportedEnvs = ['previewnet', 'testnet', 'mainnet']; +type CreateHTSParams = { + tokenName: string; + symbol: string; + treasuryAccountId: string; + initialSupply: number; + adminPrivateKey: PrivateKey; + kyc?: Key; + freeze?: Key; + customHbarFees?: number; + customTokenFees?: number; + customRoyaltyFees?: number; + customFractionalFees?: number; +}; + +type CreateNFTParams = { + tokenName: string; + symbol: string; + treasuryAccountId: string; + maxSupply: number; + adminPrivateKey: PrivateKey; + customRoyaltyFees?: number; +}; + export default class ServicesClient { static TINYBAR_TO_WEIBAR_COEF = 10_000_000_000; - private readonly DEFAULT_KEY = new Key(); + private readonly DEFAULT_KEY = PrivateKey.generateECDSA(); private readonly logger: Logger; private readonly network: string; @@ -89,7 +112,12 @@ export default class ServicesClient { return this.logger; } - async createInitialAliasAccount(providerUrl, chainId, requestId, initialBalance = 2000): Promise { + async createInitialAliasAccount( + providerUrl: string, + chainId: ethers.BigNumberish, + requestId?: string, + initialBalance: number = 2000, + ): Promise { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const privateKey = PrivateKey.generateECDSA(); const wallet = new ethers.Wallet( @@ -114,7 +142,7 @@ export default class ServicesClient { const accountId = receipt?.transfers[1].accountId!; accountId.evmAddress = EvmAddress.fromString(address); - const aliasAccount: AliasAccount = { + return { alias: accountId, accountId, address, @@ -123,11 +151,9 @@ export default class ServicesClient { wallet, keyList: KeyList.from([privateKey]), }; - - return aliasAccount; } - async executeQuery(query: Query, requestId?: string) { + async executeQuery(query: Query, requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); try { this.logger.info(`${requestIdPrefix} Execute ${query.constructor.name} query`); @@ -174,7 +200,7 @@ export default class ServicesClient { return { executedTimestamp, executedTransactionId }; } - async createToken(initialSupply = 1000, requestId?: string) { + async createToken(initialSupply: number = 1000, requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const symbol = Math.random().toString(36).slice(2, 6).toUpperCase(); this.logger.trace(`${requestIdPrefix} symbol = ${symbol}`); @@ -192,13 +218,13 @@ export default class ServicesClient { this.logger.trace(`${requestIdPrefix} get token id from receipt`); const tokenId = resp?.tokenId; this.logger.info(`${requestIdPrefix} token id = ${tokenId?.toString()}`); - return tokenId; + return tokenId!; } - async associateToken(tokenId, requestId?: string) { + async associateToken(tokenId: string | TokenId, requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); await this.executeAndGetTransactionReceipt( - await new TokenAssociateTransaction() + new TokenAssociateTransaction() .setAccountId(this._thisAccountId()) .setTokenIds([tokenId]) .setTransactionMemo('Relay test token association'), @@ -210,7 +236,7 @@ export default class ServicesClient { ); } - async transferToken(tokenId, recipient: AccountId, amount = 10, requestId?: string) { + async transferToken(tokenId: string | TokenId, recipient: AccountId, amount = 10, requestId?: string) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const receipt = await this.executeAndGetTransactionReceipt( new TransferTransaction() @@ -227,54 +253,17 @@ export default class ServicesClient { const balances = await this.executeQuery(new AccountBalanceQuery().setAccountId(recipient), requestId); this.logger.debug( - `${requestIdPrefix} Token balances for ${recipient.toString()} are ${balances.tokens.toString().toString()}`, + `${requestIdPrefix} Token balances for ${recipient.toString()} are ${balances?.tokens?.toString()}`, ); return receipt; } - async createParentContract(contractJson, requestId?: string) { - const requestIdPrefix = Utils.formatRequestIdMessage(requestId); - const contractByteCode = contractJson.deployedBytecode.replace('0x', ''); - - const fileReceipt = await this.executeAndGetTransactionReceipt( - new FileCreateTransaction() - .setKeys([this.client.operatorPublicKey || this.DEFAULT_KEY]) - .setContents(contractByteCode) - .setTransactionMemo('Relay test file create'), - requestId, - ); - - // Fetch the receipt for transaction that created the file - // The file ID is located on the transaction receipt - const fileId = fileReceipt?.fileId; - this.logger.info(`${requestIdPrefix} contract bytecode file: ${fileId?.toString()}`); - - // Create the contract - const contractReceipt = await this.executeAndGetTransactionReceipt( - new ContractCreateTransaction() - .setConstructorParameters(new ContractFunctionParameters()) - .setGas(75000) - .setInitialBalance(1) - .setBytecodeFileId(fileId || '') - .setAdminKey(this.client.operatorPublicKey || this.DEFAULT_KEY) - .setTransactionMemo('Relay test contract create'), - requestId, - ); - - // The contract ID is located on the transaction receipt - const contractId = contractReceipt?.contractId; - - this.logger.info(`${requestIdPrefix} new contract ID: ${contractId?.toString()}`); - - return contractId; - } - async executeContractCall( - contractId, + contractId: string | ContractId, functionName: string, params: ContractFunctionParameters, - gasLimit = 75000, + gasLimit: number | Long = 75000, requestId?: string, ) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); @@ -297,7 +286,7 @@ export default class ServicesClient { } async executeContractCallWithAmount( - contractId, + contractId: string | ContractId, functionName: string, params: ContractFunctionParameters, gasLimit = 500_000, @@ -316,7 +305,7 @@ export default class ServicesClient { if (amount > 0) { tx.setPayableAmount(Hbar.fromTinybars(amount)); } - let contractExecTransactionResponse; + let contractExecTransactionResponse: TransactionResponse; try { contractExecTransactionResponse = await this.executeTransaction(tx, requestId); @@ -333,20 +322,18 @@ export default class ServicesClient { } async getAliasAccountInfo( - accountId, + accountId: AccountId, privateKey: PrivateKey, - provider = null, + provider: JsonRpcProvider | null = null, requestId?: string, - keyList?: null | KeyList, + keyList?: KeyList, ): Promise { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); - //@ts-ignore - const balance = await this.executeQuery(new AccountBalanceQuery().setAccountId(accountId), requestId); + const balance = (await this.executeQuery(new AccountBalanceQuery().setAccountId(accountId), requestId))!; this.logger.info(`${requestIdPrefix} Balance of the new account: ${balance.toString()}`); - //@ts-ignore - const accountInfo = await this.executeQuery(new AccountInfoQuery().setAccountId(accountId), requestId); + const accountInfo = (await this.executeQuery(new AccountInfoQuery().setAccountId(accountId), requestId))!; this.logger.info( `${requestIdPrefix} New account Info - accountId: ${accountInfo.accountId.toString()}, evmAddress: ${ accountInfo.contractAccountId @@ -359,39 +346,40 @@ export default class ServicesClient { this.logger.child({ name: `services-client` }), ); - let wallet; + let wallet: ethers.Wallet; if (provider) { wallet = new ethers.Wallet(privateKey.toStringRaw(), provider); } else { wallet = new ethers.Wallet(privateKey.toStringRaw()); } - const account: AliasAccount = { + return { alias: accountId, accountId: accountInfo.accountId, - address: Utils.add0xPrefix(accountInfo.contractAccountId), + address: Utils.add0xPrefix(accountInfo.contractAccountId!), client: servicesClient, privateKey, wallet, keyList, }; - - return account; } // Creates an account that has 2 keys - ECDSA and a contractId. This is required for calling contract methods that create HTS tokens. // The contractId should be the id of the contract. // The account should be created after the contract has been deployed. async createAccountWithContractIdKey( - contractIdString: string, + contractId: string | ContractId, initialBalance = 10, - provider = null, + provider: JsonRpcProvider | null = null, requestId?: string, ) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const privateKey = PrivateKey.generateECDSA(); const publicKey = privateKey.publicKey; - const contractId = ContractId.fromString(contractIdString); + + if (typeof contractId === 'string') { + contractId = ContractId.fromString(contractId); + } const keys = [publicKey, contractId]; @@ -399,25 +387,28 @@ export default class ServicesClient { const keyList = new KeyList(keys, 1); this.logger.trace( - `${requestIdPrefix} Create new Eth compatible account w ContractId key: ${contractIdString}, privateKey: ${privateKey}, alias: ${publicKey.toEvmAddress()} and balance ~${initialBalance} hb`, + `${requestIdPrefix} Create new Eth compatible account w ContractId key: ${contractId}, privateKey: ${privateKey}, alias: ${publicKey.toEvmAddress()} and balance ~${initialBalance} hb`, ); - const accountCreateTx = await ( - await new AccountCreateTransaction() - .setInitialBalance(new Hbar(initialBalance)) - .setKey(keyList) - .setAlias(publicKey.toEvmAddress()) - .freezeWith(this.client) - ).sign(privateKey); + const accountCreateTx = await new AccountCreateTransaction() + .setInitialBalance(new Hbar(initialBalance)) + .setKey(keyList) + .setAlias(publicKey.toEvmAddress()) + .freezeWith(this.client) + .sign(privateKey); const txResult = await accountCreateTx.execute(this.client); const receipt = await txResult.getReceipt(this.client); - const accountId = receipt.accountId; + const accountId = receipt.accountId!; return this.getAliasAccountInfo(accountId, privateKey, provider, requestId, keyList); } - async createAliasAccount(initialBalance = 10, provider = null, requestId?: string): Promise { + async createAliasAccount( + initialBalance = 10, + provider: JsonRpcProvider | null = null, + requestId?: string, + ): Promise { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const privateKey = PrivateKey.generateECDSA(); const publicKey = privateKey.publicKey; @@ -442,7 +433,7 @@ export default class ServicesClient { } async deployContract( - contract, + contract: { bytecode: string | Uint8Array }, gas = 100_000, constructorParameters: Uint8Array = new Uint8Array(), initialBalance = 0, @@ -485,41 +476,23 @@ export default class ServicesClient { this.logger.info(`${requestIdPrefix} File ${fileId} updated with status: ${receipt.status.toString()}`); } - async getAccountBalance(account: string | AccountId, requestId?: string): Promise { - const accountId = typeof account === 'string' ? AccountId.fromString(account) : account; - return this.executeQuery(new AccountBalanceQuery().setAccountId(accountId), requestId); - } - - async getAccountBalanceInWeiBars(account: string | AccountId, requestId?: string) { - const balance = await this.getAccountBalance(account, requestId); - - return BigInt(balance.hbars.toTinybars().toString()) * BigInt(ServicesClient.TINYBAR_TO_WEIBAR_COEF); - } - getClient() { - let network = this.network; try { - network = JSON.parse(this.network); + const network = JSON.parse(this.network); + return Client.forNetwork(network); } catch (e) { // network config is a string and not a valid JSON + return Client.forName(this.network); } - - return Client.forNetwork(network); } async createHTS( - args = { + args: CreateHTSParams = { tokenName: 'Default Name', symbol: 'HTS', treasuryAccountId: '0.0.2', initialSupply: 5000, adminPrivateKey: this.DEFAULT_KEY, - kyc: null, - freeze: null, - customHbarFees: false, - customTokenFees: false, - customRoyaltyFees: false, - customFractionalFees: false, }, ) { const {} = args; @@ -549,11 +522,11 @@ export default class ServicesClient { transaction.setFreezeKey(args.freeze); } - const customFees = []; + const customFees: CustomFee[] = []; if (args.customHbarFees) { customFees.push( new CustomFixedFee() - .setHbarAmount(Hbar.from(args.customHbarFees)) + .setHbarAmount(Hbar.fromTinybars(args.customHbarFees)) .setFeeCollectorAccountId(AccountId.fromString(args.treasuryAccountId)), ); } @@ -579,7 +552,7 @@ export default class ServicesClient { transaction.setCustomFees(customFees); } - const tokenCreate = await (await transaction).execute(htsClient); + const tokenCreate = await transaction.execute(htsClient); const receipt = await tokenCreate.getReceipt(this.client); this.logger.info(`Created HTS token ${receipt.tokenId?.toString()}`); @@ -590,13 +563,12 @@ export default class ServicesClient { } async createNFT( - args = { + args: CreateNFTParams = { tokenName: 'Default Name', symbol: 'HTS', treasuryAccountId: '0.0.2', maxSupply: 5000, adminPrivateKey: this.DEFAULT_KEY, - customRoyaltyFees: false, }, ) { const {} = args; @@ -627,7 +599,7 @@ export default class ServicesClient { ]); } - let nftCreate = await (await transaction).execute(htsClient); + let nftCreate = await transaction.execute(htsClient); const receipt = await nftCreate.getReceipt(this.client); this.logger.info(`Created NFT token ${receipt.tokenId?.toString()}`); @@ -649,9 +621,10 @@ export default class ServicesClient { htsClient.setOperator(AccountId.fromString(args.treasuryAccountId), args.adminPrivateKey); // Mint new NFT - let mintTx = await ( - await new TokenMintTransaction().setTokenId(args.tokenId).setMetadata([Buffer.from(args.metadata)]) - ).execute(htsClient); + let mintTx = await new TokenMintTransaction() + .setTokenId(args.tokenId) + .setMetadata([Buffer.from(args.metadata)]) + .execute(htsClient); const receipt = await mintTx.getReceipt(this.client); return { @@ -672,9 +645,10 @@ export default class ServicesClient { htsClient.setOperator(AccountId.fromString(args.treasuryAccountId), args.adminPrivateKey); //Enable KYC flag on account and freeze the transaction for manual signing - const transaction = await ( - await new TokenGrantKycTransaction().setAccountId(args.accountId).setTokenId(args.tokenId) - ).execute(htsClient); + const transaction = await new TokenGrantKycTransaction() + .setAccountId(args.accountId) + .setTokenId(args.tokenId) + .execute(htsClient); //Request the receipt of the transaction const receipt = await transaction.getReceipt(htsClient); @@ -685,7 +659,13 @@ export default class ServicesClient { }; } - async associateHTSToken(accountId, tokenId, privateKey, htsClient, requestId?: string) { + async associateHTSToken( + accountId: string | AccountId, + tokenId: string | TokenId, + privateKey: PrivateKey, + htsClient: Client, + requestId?: string, + ) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const tokenAssociate = await ( await new TokenAssociateTransaction() @@ -699,7 +679,12 @@ export default class ServicesClient { this.logger.info(`${requestIdPrefix} HTS Token ${tokenId} associated to : ${accountId}`); } - async approveHTSToken(spenderId, tokenId, htsClient, requestId?: string) { + async approveHTSToken( + spenderId: string | AccountId, + tokenId: string | TokenId, + htsClient: Client, + requestId?: string, + ) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const amount = 10000; const tokenApprove = await new AccountAllowanceApproveTransaction() @@ -710,14 +695,19 @@ export default class ServicesClient { this.logger.info(`${requestIdPrefix} ${amount} of HTS Token ${tokenId} can be spent by ${spenderId}`); } - async transferHTSToken(accountId, tokenId, amount, fromId = this.client.operatorAccountId, requestId?: string) { + async transferHTSToken( + accountId: string | AccountId, + tokenId: string | TokenId, + amount: number | Long, + fromId: string | AccountId = this.client.operatorAccountId!, + requestId?: string, + ) { const requestIdPrefix = Utils.formatRequestIdMessage(requestId); try { - const tokenTransfer = await ( - await new TransferTransaction() - .addTokenTransfer(tokenId, fromId, -amount) - .addTokenTransfer(tokenId, accountId, amount) - ).execute(this.client); + const tokenTransfer = await new TransferTransaction() + .addTokenTransfer(tokenId, fromId, -amount) + .addTokenTransfer(tokenId, accountId, amount) + .execute(this.client); const rec = await tokenTransfer.getReceipt(this.client); this.logger.info(`${requestIdPrefix} ${amount} of HTS Token ${tokenId} can be spent by ${accountId}`); @@ -726,10 +716,4 @@ export default class ServicesClient { this.logger.error(e, `${requestIdPrefix} TransferTransaction failed`); } } - - async getAccountNonce(accountId) { - const query = new AccountInfoQuery().setAccountId(accountId); - const accountInfo = await query.execute(this.client); - return accountInfo.ethereumNonce; - } } diff --git a/packages/server/tests/helpers/utils.ts b/packages/server/tests/helpers/utils.ts index 9b20e0f9ce..755b41368b 100644 --- a/packages/server/tests/helpers/utils.ts +++ b/packages/server/tests/helpers/utils.ts @@ -34,6 +34,7 @@ import { Context } from 'mocha'; import { GitHubClient } from '../clients/githubClient'; import MirrorClient from '../clients/mirrorClient'; import { HeapDifferenceStatistics } from '../types/HeapDifferenceStatistics'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; export class Utils { static readonly HEAP_SIZE_DIFF_MEMORY_LEAK_THRESHOLD: number = 1e6; // 1 MB @@ -43,11 +44,11 @@ export class Utils { /** * Converts a number to its hexadecimal representation. * - * @param {number} num The number to convert to hexadecimal. + * @param {number | bigint | string} num The number to convert to hexadecimal. * @returns {string} The hexadecimal representation of the number. */ - static toHex = (num) => { - return parseInt(num).toString(16); + static toHex = (num: number | bigint | string): string => { + return Number(num).toString(16); }; /** @@ -56,7 +57,7 @@ export class Utils { * @param {string} id The Hedera account ID to convert. * @returns {string} The EVM compatible address. */ - static idToEvmAddress = (id): string => { + static idToEvmAddress = (id: string): string => { Assertions.assertId(id); const [shard, realm, num] = id.split('.'); @@ -71,10 +72,10 @@ export class Utils { /** * Converts a value from tinybars to weibars. * - * @param {number} value The value in tinybars to convert. - * @returns {ethers.BigNumber} The value converted to weibars. + * @param {number | bigint | string} value The value in tinybars to convert. + * @returns {bigint} The value converted to weibars. */ - static tinyBarsToWeibars = (value) => { + static tinyBarsToWeibars = (value: number | bigint | string): bigint => { return ethers.parseUnits(Number(value).toString(), 10); }; @@ -84,7 +85,7 @@ export class Utils { * @param {number} length The length of the random string to generate. * @returns {string} The generated random string. */ - static randomString(length) { + static randomString(length: number): string { let result = ''; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; for (let i = 0; i < length; i++) { @@ -109,21 +110,38 @@ export class Utils { return requestId ? `[Request ID: ${requestId}]` : ''; }; - static deployContractWithEthers = async (constructorArgs: any[] = [], contractJson, wallet, relay) => { + static deployContractWithEthers = async ( + constructorArgs: any[] = [], + contractJson: { abi: ethers.InterfaceAbi | ethers.Interface; bytecode: ethers.BytesLike | { object: string } }, + wallet: ethers.Wallet, + relay: RelayClient, + ) => { const factory = new ethers.ContractFactory(contractJson.abi, contractJson.bytecode, wallet); let contract = await factory.deploy(...constructorArgs); await contract.waitForDeployment(); // re-init the contract with the deployed address - const receipt = await relay.provider.getTransactionReceipt(contract.deploymentTransaction()?.hash); - contract = new ethers.Contract(receipt.to, contractJson.abi, wallet); + const receipt = await relay.provider.getTransactionReceipt(contract.deploymentTransaction()!.hash); - return contract; + let contractAddress: string | ethers.Addressable; + if (receipt?.to) { + // long-zero address + contractAddress = receipt.to; + } else { + // evm address + contractAddress = contract.target; + } + + return new ethers.Contract(contractAddress, contractJson.abi, wallet); }; // The main difference between this and deployContractWithEthers is that this does not re-init the contract with the deployed address // and that results in the contract address coming in EVM Format instead of LongZero format - static deployContractWithEthersV2 = async (constructorArgs: any[] = [], contractJson, wallet) => { + static deployContractWithEthersV2 = async ( + constructorArgs: any[] = [], + contractJson: { abi: ethers.Interface | ethers.InterfaceAbi; bytecode: ethers.BytesLike | { object: string } }, + wallet: ethers.Wallet, + ) => { const factory = new ethers.ContractFactory(contractJson.abi, contractJson.bytecode, wallet); const contract = await factory.deploy(...constructorArgs); await contract.waitForDeployment(); @@ -132,15 +150,15 @@ export class Utils { }; static createHTS = async ( - tokenName, - symbol, - adminAccount, - initialSupply, - abi, - associatedAccounts, - owner, - servicesNode, - requestId, + tokenName: string, + symbol: string, + adminAccount: AliasAccount, + initialSupply: number, + abi: ethers.InterfaceAbi | ethers.Interface, + associatedAccounts: AliasAccount[], + owner: AliasAccount, + servicesNode: ServicesClient, + requestId?: string, ) => { const htsResult = await servicesNode.createHTS({ tokenName, @@ -154,34 +172,35 @@ export class Utils { for (const account of associatedAccounts) { await servicesNode.associateHTSToken( account.accountId, - htsResult.receipt.tokenId, + htsResult.receipt.tokenId!, account.privateKey, htsResult.client, requestId, ); - await servicesNode.approveHTSToken(account.accountId, htsResult.receipt.tokenId, htsResult.client, requestId); + await servicesNode.approveHTSToken(account.accountId, htsResult.receipt.tokenId!, htsResult.client, requestId); } // Setup initial balance of token owner account await servicesNode.transferHTSToken( owner.accountId, - htsResult.receipt.tokenId, + htsResult.receipt.tokenId!, initialSupply, - htsResult.client, + htsResult.client.operatorAccountId!, requestId, ); - const evmAddress = Utils.idToEvmAddress(htsResult.receipt.tokenId.toString()); + const evmAddress = Utils.idToEvmAddress(htsResult.receipt.tokenId!.toString()); return new ethers.Contract(evmAddress, abi, owner.wallet); }; - static add0xPrefix = (num) => { + static add0xPrefix = (num: string) => { return num.startsWith('0x') ? num : '0x' + num; }; - static gasOptions = async (requestId, gasLimit = 1_500_000) => { + static gasOptions = async (requestId: string, gasLimit = 1_500_000) => { + const relay: RelayClient = global.relay; return { gasLimit: gasLimit, - gasPrice: await global.relay.gasPrice(requestId), + gasPrice: await relay.gasPrice(requestId), }; }; @@ -306,14 +325,18 @@ export class Utils { initialAccount: AliasAccount, neededAccounts: number, initialAmountInTinyBar: string, - requestId: string, + requestDetails: RequestDetails, ): Promise { - const requestIdPrefix = Utils.formatRequestIdMessage(requestId); const accounts: AliasAccount[] = []; for (let i = 0; i < neededAccounts; i++) { - const account = await Utils.createAliasAccount(mirrorNode, initialAccount, requestId, initialAmountInTinyBar); + const account = await Utils.createAliasAccount( + mirrorNode, + initialAccount, + requestDetails.requestId, + initialAmountInTinyBar, + ); global.logger.trace( - `${requestIdPrefix} Create new Eth compatible account w privateKey: ${account.privateKey}, alias: ${account.address} and balance ~${initialAmountInTinyBar} wei`, + `${requestDetails.formattedRequestId} Create new Eth compatible account w privateKey: ${account.privateKey}, alias: ${account.address} and balance ~${initialAmountInTinyBar} wei`, ); accounts.push(account); } diff --git a/packages/ws-server/src/controllers/eth_subscribe.ts b/packages/ws-server/src/controllers/eth_subscribe.ts index 00b6876708..87e0c243b9 100644 --- a/packages/ws-server/src/controllers/eth_subscribe.ts +++ b/packages/ws-server/src/controllers/eth_subscribe.ts @@ -24,6 +24,7 @@ import constants from '@hashgraph/json-rpc-relay/dist/lib/constants'; import { MirrorNodeClient } from '@hashgraph/json-rpc-relay/dist/lib/clients'; import jsonResp from '@hashgraph/json-rpc-server/dist/koaJsonRpc/lib/RpcResponse'; import { constructValidLogSubscriptionFilter, getMultipleAddressesEnabled } from '../utils/utils'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; /** * Subscribes to new block headers (newHeads) events and returns the response and subscription ID. @@ -61,6 +62,7 @@ const subscribeToNewHeads = ( * @param {string} event - The event name to subscribe to (e.g., "newHeads"). * @param {Relay} relay - The relay object used for managing WebSocket subscriptions. * @param {any} logger - The logger object used for logging subscription information. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {{ response: any; subscriptionId: any }} Returns an object containing the response and subscription ID. */ const handleEthSubscribeNewHeads = ( @@ -73,7 +75,7 @@ const handleEthSubscribeNewHeads = ( relay: Relay, logger: any, connectionIdPrefix: string, - requestIdPrefix: string, + requestDetails: RequestDetails, ): { response: any; subscriptionId: any } => { const wsNewHeadsEnabled = typeof process.env.WS_NEW_HEADS_ENABLED !== 'undefined' ? process.env.WS_NEW_HEADS_ENABLED === 'true' : true; @@ -82,7 +84,7 @@ const handleEthSubscribeNewHeads = ( ({ response, subscriptionId } = subscribeToNewHeads(filters, response, subscriptionId, ctx, event, relay, logger)); } else { logger.warn( - `${connectionIdPrefix} ${requestIdPrefix}: Unsupported JSON-RPC method due to the value of environment variable WS_NEW_HEADS_ENABLED`, + `${connectionIdPrefix} ${requestDetails.formattedRequestId}: Unsupported JSON-RPC method due to the value of environment variable WS_NEW_HEADS_ENABLED`, ); response = jsonResp(request.id, predefined.UNSUPPORTED_METHOD, undefined); } @@ -94,7 +96,6 @@ const handleEthSubscribeNewHeads = ( * Validates the subscription parameters, checks if multiple addresses are enabled, * and subscribes to the event or sends an error response accordingly. * @param {any} filters - The filters object specifying criteria for the subscription. - * @param {string} requestIdPrefix - The prefix for the request ID. * @param {any} response - The response object to be sent to the client. * @param {any} request - The request object received from the client. * @param {any} subscriptionId - The ID of the subscription. @@ -102,11 +103,11 @@ const handleEthSubscribeNewHeads = ( * @param {any} event - The event name to subscribe to. * @param {Relay} relay - The relay object used for managing WebSocket subscriptions. * @param {MirrorNodeClient} mirrorNodeClient - The client for interacting with the MirrorNode API. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {{ response: any; subscriptionId: any }} Returns an object containing the response and subscription ID. */ const handleEthSubscribeLogs = async ( filters: any, - requestIdPrefix: string, response: any, request: any, subscriptionId: any, @@ -114,10 +115,11 @@ const handleEthSubscribeLogs = async ( event: any, relay: Relay, mirrorNodeClient: MirrorNodeClient, + requestDetails: RequestDetails, ): Promise<{ response: any; subscriptionId: any }> => { const validFiltersObject = constructValidLogSubscriptionFilter(filters); - await validateSubscribeEthLogsParams(validFiltersObject, requestIdPrefix, mirrorNodeClient); + await validateSubscribeEthLogsParams(validFiltersObject, mirrorNodeClient, requestDetails); if ( !getMultipleAddressesEnabled() && Array.isArray(validFiltersObject['address']) && @@ -140,24 +142,24 @@ const handleEthSubscribeLogs = async ( * @param {object} args - An object containing the function parameters as properties. * @param {any} args.ctx - The context object containing information about the WebSocket connection. * @param {any[]} args.params - The parameters of the method request, expecting an event and filters. - * @param {string} args.requestIdPrefix - The prefix for the request ID. * @param {any} args.request - The request object received from the client. * @param {Relay} args.relay - The relay object for interacting with the Hedera network. * @param {MirrorNodeClient} args.mirrorNodeClient - The mirror node client for handling subscriptions. * @param {ConnectionLimiter} args.limiter - The limiter object for managing connection subscriptions. * @param {any} args.logger - The logger object for logging messages and events. + * @param {RequestDetails} args.requestDetails - The request details for logging and tracking. * @returns {Promise} Returns a promise that resolves with the subscription response. */ export const handleEthSubsribe = async ({ ctx, params, - requestIdPrefix, request, relay, mirrorNodeClient, limiter, logger, connectionIdPrefix, + requestDetails, }): Promise => { const event = params[0]; const filters = params[1]; @@ -168,7 +170,6 @@ export const handleEthSubsribe = async ({ case constants.SUBSCRIBE_EVENTS.LOGS: ({ response, subscriptionId } = await handleEthSubscribeLogs( filters, - requestIdPrefix, response, request, subscriptionId, @@ -176,6 +177,7 @@ export const handleEthSubsribe = async ({ event, relay, mirrorNodeClient, + requestDetails, )); break; @@ -190,7 +192,7 @@ export const handleEthSubsribe = async ({ relay, logger, connectionIdPrefix, - requestIdPrefix, + requestDetails, )); break; case constants.SUBSCRIBE_EVENTS.NEW_PENDING_TRANSACTIONS: diff --git a/packages/ws-server/src/controllers/index.ts b/packages/ws-server/src/controllers/index.ts index f4b90a8a72..526ad24c6f 100644 --- a/packages/ws-server/src/controllers/index.ts +++ b/packages/ws-server/src/controllers/index.ts @@ -32,6 +32,7 @@ import { MethodNotFound, IPRateLimitExceeded, } from '@hashgraph/json-rpc-server/dist/koaJsonRpc/lib/RpcError'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; /** * Handles sending requests to a Relay by calling a specified method with given parameters. @@ -43,8 +44,8 @@ import { * @param {any} args.params - The parameters for the method call. * @param {Relay} args.relay - The relay object. * @param {any} args.logger - The logger object used for tracing. - * @param {string} args.requestIdPrefix - Prefix for request ID used for logging. * @param {string} args.connectionIdPrefix - Prefix for connection ID used for logging. + * @param {RequestDetails} args.requestDetails - The request details for logging and tracking. * @returns {Promise} A promise that resolves to the result of the request. */ const handleSendingRequestsToRelay = async ({ @@ -53,15 +54,29 @@ const handleSendingRequestsToRelay = async ({ params, relay, logger, - requestIdPrefix, connectionIdPrefix, + ctx, + requestDetails, }): Promise => { - logger.trace(`${connectionIdPrefix} ${requestIdPrefix}: Submitting request=${JSON.stringify(request)} to relay.`); - + logger.trace( + `${connectionIdPrefix} ${requestDetails.formattedRequestId}: Submitting request=${JSON.stringify( + request, + )} to relay.`, + ); try { const resolvedParams = resolveParams(method, params); const [service, methodName] = method.split('_'); + // Rearrange the parameters for certain methods, since not everywhere requestDetails is last aparameter + const paramRearrangementMap: { [key: string]: (params: any[], requestDetails: RequestDetails) => any[] } = { + estimateGas: (params, requestDetails) => [...params, null, requestDetails], + getStorageAt: (params, requestDetails) => [params[0], params[1], requestDetails, params[2]], + default: (params, requestDetails) => [...params, requestDetails], + }; + + const rearrangeParams = paramRearrangementMap[methodName] || paramRearrangementMap['default']; + const rearrangedParams = rearrangeParams(resolvedParams, requestDetails); + // Call the relay method with the resolved parameters. // Method will be validated by "verifySupportedMethod" before reaching this point. let txRes: any; @@ -69,16 +84,16 @@ const handleSendingRequestsToRelay = async ({ txRes = await relay .eth() .filterService() - [methodName](...resolvedParams, requestIdPrefix); + [methodName](...rearrangedParams); } else { - txRes = await relay[service]()[methodName](...resolvedParams, requestIdPrefix); + txRes = await relay[service]()[methodName](...rearrangedParams); } if (!txRes) { logger.trace( - `${connectionIdPrefix} ${requestIdPrefix}: Fail to retrieve result for request=${JSON.stringify( - request, - )}. Result=${txRes}`, + `${connectionIdPrefix} ${ + requestDetails.formattedRequestId + }: Fail to retrieve result for request=${JSON.stringify(request)}. Result=${txRes}`, ); } @@ -100,10 +115,10 @@ const handleSendingRequestsToRelay = async ({ * @param {any} logger - The logger object. * @param {any} request - The request object. * @param {ConnectionLimiter} limiter - The connection limiter object. - * @param {string} requestIdPrefix - Prefix for request ID. * @param {string} connectionIdPrefix - Prefix for connection ID. * @param {MirrorNodeClient} mirrorNodeClient - The MirrorNodeClient object. * @param {WsMetricRegistry} wsMetricRegistry - The WsMetricRegistry object. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @returns {Promise} A promise that resolves to the response of the request. */ export const getRequestResult = async ( @@ -112,10 +127,10 @@ export const getRequestResult = async ( logger: any, request: any, limiter: ConnectionLimiter, - requestIdPrefix: string, connectionIdPrefix: string, mirrorNodeClient: MirrorNodeClient, wsMetricRegistry: WsMetricRegistry, + requestDetails: RequestDetails, ): Promise => { // Extract the method and parameters from the received request let { method, params } = request; @@ -128,6 +143,7 @@ export const getRequestResult = async ( wsMetricRegistry.getCounter('methodsCounterByIp').labels(ctx.request.ip, method).inc(); // validate request's jsonrpc object + const requestIdPrefix = requestDetails.formattedRequestId; if (!validateJsonRpcRequest(request, logger, requestIdPrefix, connectionIdPrefix)) { return jsonResp(request.id || null, new InvalidRequest(), undefined); } @@ -176,9 +192,9 @@ export const getRequestResult = async ( request, method, limiter, - requestIdPrefix, mirrorNodeClient, connectionIdPrefix, + requestDetails, }; switch (method) { diff --git a/packages/ws-server/src/utils/validators.ts b/packages/ws-server/src/utils/validators.ts index f157bd2586..154bd956da 100644 --- a/packages/ws-server/src/utils/validators.ts +++ b/packages/ws-server/src/utils/validators.ts @@ -22,25 +22,26 @@ import constants from '@hashgraph/json-rpc-relay/dist/lib/constants'; import { JsonRpcError, predefined } from '@hashgraph/json-rpc-relay'; import { MirrorNodeClient } from '@hashgraph/json-rpc-relay/dist/lib/clients'; import { EthSubscribeLogsParamsObject } from '@hashgraph/json-rpc-server/dist/validator'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; /** * Validates whether the provided address corresponds to a contract or token type. * Throws an error if the address is not a valid contract or token type or does not exist. * @param {string} address - The address to validate. - * @param {string} requestId - The unique identifier for the request. * @param {MirrorNodeClient} mirrorNodeClient - The client for interacting with the MirrorNode API. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. * @throws {JsonRpcError} Throws a JsonRpcError if the address is not a valid contract or token type or does not exist. */ const validateIsContractOrTokenAddress = async ( address: string, - requestId: string, mirrorNodeClient: MirrorNodeClient, + requestDetails: RequestDetails, ) => { const isContractOrToken = await mirrorNodeClient.resolveEntityType( address, - [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], constants.METHODS.ETH_SUBSCRIBE, - requestId, + requestDetails, + [constants.TYPE_CONTRACT, constants.TYPE_TOKEN], ); if (!isContractOrToken) { throw new JsonRpcError( @@ -48,7 +49,7 @@ const validateIsContractOrTokenAddress = async ( 'filters.address', `${address} is not a valid contract or token type or does not exists`, ), - requestId, + requestDetails.formattedRequestId, ); } }; @@ -56,13 +57,13 @@ const validateIsContractOrTokenAddress = async ( /** * Validates the parameters for subscribing to ETH logs. * @param {any} filters - The filters object containing parameters for subscribing to ETH logs. - * @param {string} requestId - The unique identifier for the request. * @param {MirrorNodeClient} mirrorNodeClient - The client for interacting with the MirrorNode API. + * @param {RequestDetails} requestDetails - The request details for logging and tracking. */ export const validateSubscribeEthLogsParams = async ( filters: any, - requestId: string, mirrorNodeClient: MirrorNodeClient, + requestDetails: RequestDetails, ) => { // validate address exists and is correct length and type // validate topics if exists and is array and each one is correct length and type @@ -73,10 +74,10 @@ export const validateSubscribeEthLogsParams = async ( if (ethSubscribeLogsParams.object.address) { if (Array.isArray(ethSubscribeLogsParams.object.address)) { for (const address of ethSubscribeLogsParams.object.address) { - await validateIsContractOrTokenAddress(address, requestId, mirrorNodeClient); + await validateIsContractOrTokenAddress(address, mirrorNodeClient, requestDetails); } } else { - await validateIsContractOrTokenAddress(ethSubscribeLogsParams.object.address, requestId, mirrorNodeClient); + await validateIsContractOrTokenAddress(ethSubscribeLogsParams.object.address, mirrorNodeClient, requestDetails); } } }; diff --git a/packages/ws-server/src/webSocketServer.ts b/packages/ws-server/src/webSocketServer.ts index a9ec9ca59f..7fd5dc80a1 100644 --- a/packages/ws-server/src/webSocketServer.ts +++ b/packages/ws-server/src/webSocketServer.ts @@ -34,6 +34,7 @@ import KoaJsonRpc from '@hashgraph/json-rpc-server/dist/koaJsonRpc'; import jsonResp from '@hashgraph/json-rpc-server/dist/koaJsonRpc/lib/RpcResponse'; import { JsonRpcError, predefined, type Relay, RelayImpl } from '@hashgraph/json-rpc-relay'; import { getBatchRequestsMaxSize, getWsBatchRequestsEnabled, handleConnectionClose, sendToClient } from './utils/utils'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; dotenv.config({ path: path.resolve(__dirname, '../../../.env') }); @@ -59,7 +60,7 @@ const wsMetricRegistry = new WsMetricRegistry(register); const pingInterval = Number(process.env.WS_PING_INTERVAL || 100000); const app = websockify(new Koa()); -app.ws.use(async (ctx) => { +app.ws.use(async (ctx: Koa.Context) => { // Increment the total opened connections wsMetricRegistry.getCounter('totalOpenedConnections').inc(); @@ -72,8 +73,10 @@ app.ws.use(async (ctx) => { ctx.websocket.limiter = limiter; ctx.websocket.wsMetricRegistry = wsMetricRegistry; const connectionIdPrefix = formatIdMessage('Connection ID', ctx.websocket.id); - const requestIdPrefix = formatIdMessage('Request ID', ctx.websocket.requestId); + const requestDetails = new RequestDetails({ requestId: ctx.websocket.requestId, ipAddress: ctx.request.ip }); + const requestIdPrefix = requestDetails.formattedRequestId; logger.info( + // @ts-ignore `${connectionIdPrefix} ${requestIdPrefix} New connection established. Current active connections: ${ctx.app.server._connections}`, ); @@ -153,10 +156,10 @@ app.ws.use(async (ctx) => { logger, item, limiter, - requestIdPrefix, connectionIdPrefix, mirrorNodeClient, wsMetricRegistry, + requestDetails, ); }); @@ -175,10 +178,10 @@ app.ws.use(async (ctx) => { logger, request, limiter, - requestIdPrefix, connectionIdPrefix, mirrorNodeClient, wsMetricRegistry, + requestDetails, ); // send to client @@ -201,10 +204,11 @@ app.ws.use(async (ctx) => { } }); -const httpApp = new KoaJsonRpc(logger, register).getKoaApp(); +const koaJsonRpc = new KoaJsonRpc(logger, register); +const httpApp = koaJsonRpc.getKoaApp(); collectDefaultMetrics({ register, prefix: 'rpc_relay_' }); -httpApp.use(async (ctx, next) => { +httpApp.use(async (ctx: Koa.Context, next: Koa.Next) => { // prometheus metrics exposure if (ctx.url === '/metrics') { ctx.status = 200; @@ -215,7 +219,7 @@ httpApp.use(async (ctx, next) => { } else if (ctx.url === '/health/readiness') { // readiness endpoint try { - const result = relay.eth().chainId(); + const result = relay.eth().chainId(koaJsonRpc.getRequestDetails()); if (result.includes('0x12')) { ctx.status = 200; ctx.body = 'OK'; diff --git a/packages/ws-server/tests/acceptance/call.spec.ts b/packages/ws-server/tests/acceptance/call.spec.ts index a00ae92d04..a1f6a17948 100644 --- a/packages/ws-server/tests/acceptance/call.spec.ts +++ b/packages/ws-server/tests/acceptance/call.spec.ts @@ -25,6 +25,7 @@ import { WsTestConstant, WsTestHelper } from '../helper'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import ERC20MockJson from '@hashgraph/json-rpc-server/tests/contracts/ERC20Mock.json'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-1 eth_call', async function () { const METHOD_NAME = 'eth_call'; @@ -60,14 +61,14 @@ describe('@web-socket-batch-1 eth_call', async function () { // @ts-ignore const { mirrorNode } = global; - let requestId: string, - erc20TokenAddr: string, + let erc20TokenAddr: string, accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider, erc20EtherInterface: ethers.Interface; + const requestDetails = new RequestDetails({ requestId: 'ws_callTest', ipAddress: '0.0.0.0' }); + before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '2500000000'; //25 Hbar @@ -78,7 +79,7 @@ describe('@web-socket-batch-1 eth_call', async function () { initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); diff --git a/packages/ws-server/tests/acceptance/estimateGas.spec.ts b/packages/ws-server/tests/acceptance/estimateGas.spec.ts index 62cab70515..5da309f6ba 100644 --- a/packages/ws-server/tests/acceptance/estimateGas.spec.ts +++ b/packages/ws-server/tests/acceptance/estimateGas.spec.ts @@ -25,6 +25,7 @@ import { WsTestConstant, WsTestHelper } from '../helper'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import basicContractJson from '@hashgraph/json-rpc-server/tests/contracts/Basic.json'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-1 eth_estimateGas', async function () { const METHOD_NAME = 'eth_estimateGas'; @@ -38,22 +39,22 @@ describe('@web-socket-batch-1 eth_estimateGas', async function () { currentPrice: number, expectedGas: number, gasPriceDeviation: number, - ethersWsProvider: WebSocketProvider, - requestId = 'eth_estimateGas'; + ethersWsProvider: WebSocketProvider; + + const requestDetails = new RequestDetails({ requestId: 'ws_estimateGasTest', ipAddress: '0.0.0.0' }); before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '2500000000'; //25 Hbar - const neededAccounts: number = 1; + accounts.push( ...(await Utils.createMultipleAliasAccounts( mirrorNode, initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); diff --git a/packages/ws-server/tests/acceptance/getBalance.spec.ts b/packages/ws-server/tests/acceptance/getBalance.spec.ts index d4647306eb..1019b2d733 100644 --- a/packages/ws-server/tests/acceptance/getBalance.spec.ts +++ b/packages/ws-server/tests/acceptance/getBalance.spec.ts @@ -24,6 +24,8 @@ import { ethers, WebSocketProvider } from 'ethers'; import { WsTestConstant, WsTestHelper } from '../helper'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-1 eth_getBalance', async function () { const METHOD_NAME = 'eth_getBalance'; @@ -38,13 +40,14 @@ describe('@web-socket-batch-1 eth_getBalance', async function () { [WsTestConstant.FAKE_TX_HASH, '0xhbar', 36], ]; // @ts-ignore - const { mirrorNode } = global; + const { mirrorNode }: { mirrorNode: MirrorClient } = global; + const requestId = 'getBalanceTest_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); + let accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '2500000000'; //25 Hbar @@ -55,7 +58,7 @@ describe('@web-socket-batch-1 eth_getBalance', async function () { initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); diff --git a/packages/ws-server/tests/acceptance/getLogs.spec.ts b/packages/ws-server/tests/acceptance/getLogs.spec.ts index fea30b0a0c..1b130edff3 100644 --- a/packages/ws-server/tests/acceptance/getLogs.spec.ts +++ b/packages/ws-server/tests/acceptance/getLogs.spec.ts @@ -24,6 +24,8 @@ import { ethers, WebSocketProvider } from 'ethers'; import { WsTestConstant, WsTestHelper } from '../helper'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-2 eth_getLogs', async function () { const EXPECTED_VALUE = 7; @@ -77,13 +79,15 @@ describe('@web-socket-batch-2 eth_getLogs', async function () { const SIMPLE_CONTRACT_BYTECODE = '0x6080604052348015600f57600080fd5b507f4e7df42af9a017b7c655a28ef10cbc8f05b2b088f087ee02416cfa1a96ac3be26007604051603e91906091565b60405180910390a160aa565b6000819050919050565b6000819050919050565b6000819050919050565b6000607d6079607584604a565b605e565b6054565b9050919050565b608b816068565b82525050565b600060208201905060a460008301846084565b92915050565b603f8060b76000396000f3fe6080604052600080fdfea264697066735822122084db7fe76bde5c9c041d61bb40294c56dc6d339bdbc8e0cd285fc4008ccefc2c64736f6c63430008180033'; // @ts-ignore - const { mirrorNode } = global; + const { mirrorNode }: { mirrorNode: MirrorClient } = global; + const requestId = 'getLogsTest_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); + let wsFilterObj: any, accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; + before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '2500000000'; //25 Hbar @@ -94,7 +98,7 @@ describe('@web-socket-batch-2 eth_getLogs', async function () { initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); diff --git a/packages/ws-server/tests/acceptance/getStorageAt.spec.ts b/packages/ws-server/tests/acceptance/getStorageAt.spec.ts index 011dadfd92..dddcb0255e 100644 --- a/packages/ws-server/tests/acceptance/getStorageAt.spec.ts +++ b/packages/ws-server/tests/acceptance/getStorageAt.spec.ts @@ -24,6 +24,7 @@ import { ethers, WebSocketProvider } from 'ethers'; import { WsTestConstant, WsTestHelper } from '../helper'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-2 eth_getStorageAt', async function () { const METHOD_NAME = 'eth_getStorageAt'; @@ -36,7 +37,6 @@ describe('@web-socket-batch-2 eth_getStorageAt', async function () { [WsTestConstant.FAKE_TX_HASH, ''], [WsTestConstant.FAKE_TX_HASH, 36, 'latest'], [WsTestConstant.FAKE_TX_HASH, '0xhbar', 'latest'], - [WsTestConstant.FAKE_TX_HASH, '0x0', 'latest', '0xhedera'], ]; // @notice: The simple contract artifacts (ABI & bytecode) below simply has one state at position 0, which will be assigned to the number `7` within the consutrctor after deployment @@ -54,10 +54,10 @@ describe('@web-socket-batch-2 eth_getStorageAt', async function () { let params: any[], accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; + + const requestDetails = new RequestDetails({ requestId: 'ws_getStorageAtTest', ipAddress: '0.0.0.0' }); before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '2500000000'; //25 Hbar @@ -68,7 +68,7 @@ describe('@web-socket-batch-2 eth_getStorageAt', async function () { initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); diff --git a/packages/ws-server/tests/acceptance/getTransactionByHash.spec.ts b/packages/ws-server/tests/acceptance/getTransactionByHash.spec.ts index 3e681433df..c3dee35c12 100644 --- a/packages/ws-server/tests/acceptance/getTransactionByHash.spec.ts +++ b/packages/ws-server/tests/acceptance/getTransactionByHash.spec.ts @@ -26,6 +26,9 @@ import { numberTo0x } from '@hashgraph/json-rpc-relay/src/formatters'; import { ONE_TINYBAR_IN_WEI_HEX } from '@hashgraph/json-rpc-relay/tests/lib/eth/eth-config'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-2 eth_getTransactionByHash', async function () { const METHOD_NAME = 'eth_getTransactionByHash'; @@ -45,15 +48,16 @@ describe('@web-socket-batch-2 eth_getTransactionByHash', async function () { ]; // @ts-ignore - const { mirrorNode, relay, initialBalance } = global; + const { mirrorNode, relay, initialBalance }: { mirrorNode: MirrorClient; relay: RelayClient } = global; + const requestId = 'getTransactionByHash_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); + let txHash: string, expectedTxReceipt: any, accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const neededAccounts: number = 2; @@ -63,7 +67,7 @@ describe('@web-socket-batch-2 eth_getTransactionByHash', async function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -73,12 +77,12 @@ describe('@web-socket-batch-2 eth_getTransactionByHash', async function () { gasLimit: numberTo0x(30000), chainId: Number(CHAIN_ID), to: accounts[1].address, - nonce: await relay.getAccountNonce(accounts[0].address), - maxFeePerGas: await relay.gasPrice(), + nonce: await relay.getAccountNonce(accounts[0].address, requestId), + maxFeePerGas: await relay.gasPrice(requestId), }; const signedTx = await accounts[0].wallet.signTransaction(tx); - txHash = await relay.sendRawTransaction(signedTx); - expectedTxReceipt = await mirrorNode.get(`/contracts/results/${txHash}`); + txHash = await relay.sendRawTransaction(signedTx, requestId); + expectedTxReceipt = await mirrorNode.get(`/contracts/results/${txHash}`, requestDetails); }); beforeEach(async () => { diff --git a/packages/ws-server/tests/acceptance/getTransactionCount.spec.ts b/packages/ws-server/tests/acceptance/getTransactionCount.spec.ts index ac74249c2a..80e16363d1 100644 --- a/packages/ws-server/tests/acceptance/getTransactionCount.spec.ts +++ b/packages/ws-server/tests/acceptance/getTransactionCount.spec.ts @@ -25,6 +25,9 @@ import { WsTestConstant, WsTestHelper } from '../helper'; import { numberTo0x } from '@hashgraph/json-rpc-relay/src/formatters'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@release @web-socket-batch-2 eth_getTransactionCount', async function () { const METHOD_NAME = 'eth_getTransactionCount'; @@ -32,14 +35,14 @@ describe('@release @web-socket-batch-2 eth_getTransactionCount', async function const ONE_TINYBAR = Utils.add0xPrefix(Utils.toHex(ethers.parseUnits('1', 10))); // @ts-ignore - const { mirrorNode, relay } = global; + const { mirrorNode, relay }: { mirrorNode: MirrorClient; relay: RelayClient } = global; + const requestId = 'getTransactionCount_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); - let requestId: string, - accounts: AliasAccount[] = [], + let accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const initialAmount: string = '100000000'; //1 Hbar @@ -50,7 +53,7 @@ describe('@release @web-socket-batch-2 eth_getTransactionCount', async function initialAccount, neededAccounts, initialAmount, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -85,7 +88,7 @@ describe('@release @web-socket-batch-2 eth_getTransactionCount', async function 'latest', ]); WsTestHelper.assertJsonRpcObject(beforeSendRawTransactionCountResponse); - const transactionCountBefore = await relay.getAccountNonce(accounts[0].address); + const transactionCountBefore = await relay.getAccountNonce(accounts[0].address, requestId); expect(Number(beforeSendRawTransactionCountResponse.result)).to.eq(transactionCountBefore); const transaction = { @@ -94,7 +97,7 @@ describe('@release @web-socket-batch-2 eth_getTransactionCount', async function chainId: Number(CHAIN_ID), to: accounts[1].address, maxFeePerGas: defaultGasPrice, - nonce: await relay.getAccountNonce(accounts[0].address), + nonce: await relay.getAccountNonce(accounts[0].address, requestId), }; const signedTx = await accounts[0].wallet.signTransaction(transaction); // @notice submit a transaction to increase transaction count @@ -105,7 +108,7 @@ describe('@release @web-socket-batch-2 eth_getTransactionCount', async function 'latest', ]); WsTestHelper.assertJsonRpcObject(afterSendRawTransactionCountResponse); - const transactionCountAfter = await relay.getAccountNonce(accounts[0].address); + const transactionCountAfter = await relay.getAccountNonce(accounts[0].address, requestId); expect(Number(afterSendRawTransactionCountResponse.result)).to.eq(transactionCountAfter); }); }); diff --git a/packages/ws-server/tests/acceptance/getTransactionReceipt.spec.ts b/packages/ws-server/tests/acceptance/getTransactionReceipt.spec.ts index 273512c5d9..12bed02289 100644 --- a/packages/ws-server/tests/acceptance/getTransactionReceipt.spec.ts +++ b/packages/ws-server/tests/acceptance/getTransactionReceipt.spec.ts @@ -26,6 +26,9 @@ import { numberTo0x } from '@hashgraph/json-rpc-relay/src/formatters'; import { ONE_TINYBAR_IN_WEI_HEX } from '@hashgraph/json-rpc-relay/tests/lib/eth/eth-config'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-2 eth_getTransactionReceipt', async function () { const METHOD_NAME = 'eth_getTransactionReceipt'; @@ -45,16 +48,16 @@ describe('@web-socket-batch-2 eth_getTransactionReceipt', async function () { ]; // @ts-ignore - const { mirrorNode, relay, initialBalance } = global; + const { mirrorNode, relay, initialBalance }: { mirrorNode: MirrorClient; relay: RelayClient } = global; + const requestId = 'getTransactionReceiptTest_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); let txHash: string, expectedTxReceipt: any, accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const neededAccounts: number = 3; @@ -64,7 +67,7 @@ describe('@web-socket-batch-2 eth_getTransactionReceipt', async function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -74,13 +77,13 @@ describe('@web-socket-batch-2 eth_getTransactionReceipt', async function () { gasLimit: numberTo0x(30000), chainId: Number(CHAIN_ID), to: accounts[1].address, - nonce: await relay.getAccountNonce(accounts[0].address), - maxFeePerGas: await relay.gasPrice(), + nonce: await relay.getAccountNonce(accounts[0].address, requestId), + maxFeePerGas: await relay.gasPrice(requestId), }; const signedTx = await accounts[0].wallet.signTransaction(tx); - txHash = await relay.sendRawTransaction(signedTx); - expectedTxReceipt = await mirrorNode.get(`/contracts/results/${txHash}`); + txHash = await relay.sendRawTransaction(signedTx, requestId); + expectedTxReceipt = await mirrorNode.get(`/contracts/results/${txHash}`, requestDetails); }); beforeEach(async () => { diff --git a/packages/ws-server/tests/acceptance/index.spec.ts b/packages/ws-server/tests/acceptance/index.spec.ts index 6ece830f78..b35a9be4c0 100644 --- a/packages/ws-server/tests/acceptance/index.spec.ts +++ b/packages/ws-server/tests/acceptance/index.spec.ts @@ -35,6 +35,8 @@ import { app as wsApp } from '@hashgraph/json-rpc-ws-server/dist/webSocketServer import ServicesClient from '@hashgraph/json-rpc-server/tests/clients/servicesClient'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; +import { Server } from 'node:http'; dotenv.config({ path: path.resolve(__dirname, '../../../.env') }); const testLogger = pino({ @@ -63,8 +65,7 @@ global.relayIsLocal = RELAY_URL === LOCAL_RELAY_URL; describe('RPC Server Acceptance Tests', function () { this.timeout(240 * 1000); // 240 seconds - let relayServer; // Relay Server - let socketServer; + const requestDetails = new RequestDetails({ requestId: 'rpc_batch1Test', ipAddress: '0.0.0.0' }); global.servicesNode = new ServicesClient( NETWORK, OPERATOR_ID, @@ -73,8 +74,6 @@ describe('RPC Server Acceptance Tests', function () { ); global.mirrorNode = new MirrorClient(MIRROR_NODE_URL, logger.child({ name: `mirror-node-test-client` })); global.relay = new RelayClient(RELAY_URL, logger.child({ name: `relay-test-client` })); - global.relayServer = relayServer; - global.socketServer = socketServer; global.logger = logger; before(async () => { @@ -100,7 +99,7 @@ describe('RPC Server Acceptance Tests', function () { ); global.accounts = new Array(initialAccount); - await global.mirrorNode.get(`/accounts/${initialAccount.address}`); + await global.mirrorNode.get(`/accounts/${initialAccount.address}`, requestDetails); }); after(async function () { @@ -139,10 +138,12 @@ describe('RPC Server Acceptance Tests', function () { //stop relay logger.info('Stop relay'); + const relayServer: Server = global.relayServer; if (relayServer !== undefined) { relayServer.close(); } + const socketServer: Server = global.socketServer; if (process.env.TEST_WS_SERVER === 'true' && socketServer !== undefined) { socketServer.close(); } @@ -170,7 +171,7 @@ describe('RPC Server Acceptance Tests', function () { // start local relay, relay instance in local should not be running logger.info(`Start relay on port ${constants.RELAY_PORT}`); - relayServer = app.listen({ port: constants.RELAY_PORT }); + const relayServer = app.listen({ port: constants.RELAY_PORT }); if (process.env.TEST_WS_SERVER === 'true') { logger.info(`Start ws-server on port ${constants.WEB_SOCKET_PORT}`); diff --git a/packages/ws-server/tests/acceptance/sendRawTransaction.spec.ts b/packages/ws-server/tests/acceptance/sendRawTransaction.spec.ts index 61f7588234..9d46bd5e6b 100644 --- a/packages/ws-server/tests/acceptance/sendRawTransaction.spec.ts +++ b/packages/ws-server/tests/acceptance/sendRawTransaction.spec.ts @@ -28,6 +28,9 @@ import { numberTo0x } from '@hashgraph/json-rpc-relay/src/formatters'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; import { ONE_TINYBAR_IN_WEI_HEX } from '@hashgraph/json-rpc-relay/tests/lib/eth/eth-config'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient'; +import { RequestDetails } from '@hashgraph/json-rpc-relay/dist/lib/types'; describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { const METHOD_NAME = 'eth_sendRawTransaction'; @@ -47,16 +50,17 @@ describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { ]; // @ts-ignore - const { mirrorNode, relay } = global; + const { mirrorNode, relay }: { mirrorNode: MirrorClient; relay: RelayClient } = global; const initialBalance = '5000000000'; // 50hbar + const requestId = 'sendRawTransactionTest_ws-server'; + const requestDetails = new RequestDetails({ requestId: requestId, ipAddress: '0.0.0.0' }); let tx: any, sendHbarToProxyContractDeployerTx: any, accounts: AliasAccount[] = [], ethersWsProvider: WebSocketProvider; - let requestId: string; + before(async () => { - requestId = Utils.generateRequestId(); const initialAccount: AliasAccount = global.accounts[0]; const neededAccounts: number = 3; @@ -66,7 +70,7 @@ describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { initialAccount, neededAccounts, initialBalance, - requestId, + requestDetails, )), ); global.accounts.push(...accounts); @@ -76,7 +80,7 @@ describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { gasLimit: numberTo0x(30000), chainId: Number(CHAIN_ID), to: accounts[2].address, - maxFeePerGas: await relay.gasPrice(), + maxFeePerGas: await relay.gasPrice(requestId), }; sendHbarToProxyContractDeployerTx = { @@ -110,15 +114,15 @@ describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { } it(`@release Should execute eth_sendRawTransaction on Standard Web Socket and handle valid requests correctly`, async () => { - tx.nonce = await relay.getAccountNonce(accounts[0].address); + tx.nonce = await relay.getAccountNonce(accounts[0].address, requestId); const signedTx = await accounts[0].wallet.signTransaction(tx); const response = await WsTestHelper.sendRequestToStandardWebSocket(METHOD_NAME, [signedTx], 1000); WsTestHelper.assertJsonRpcObject(response); const txHash = response.result; - const txReceipt = await mirrorNode.get(`/contracts/results/${txHash}`); - const fromAccountInfo = await mirrorNode.get(`/accounts/${txReceipt.from}`); + const txReceipt = await mirrorNode.get(`/contracts/results/${txHash}`, requestDetails); + const fromAccountInfo = await mirrorNode.get(`/accounts/${txReceipt.from}`, requestDetails); expect(txReceipt.to).to.eq(accounts[2].address.toLowerCase()); expect(fromAccountInfo.evm_address).to.eq(accounts[0].address.toLowerCase()); @@ -178,13 +182,13 @@ describe('@web-socket-batch-2 eth_sendRawTransaction', async function () { } it(`@release Should execute eth_sendRawTransaction on Ethers Web Socket Provider and handle valid requests correctly`, async () => { - tx.nonce = await relay.getAccountNonce(accounts[1].address); + tx.nonce = await relay.getAccountNonce(accounts[1].address, requestId); const signedTx = await accounts[1].wallet.signTransaction(tx); // const signedTx = await accounts[0].wallet.signTransaction(tx); const txHash = await ethersWsProvider.send(METHOD_NAME, [signedTx]); - const txReceipt = await mirrorNode.get(`/contracts/results/${txHash}`); - const fromAccountInfo = await mirrorNode.get(`/accounts/${txReceipt.from}`); + const txReceipt = await mirrorNode.get(`/contracts/results/${txHash}`, requestDetails); + const fromAccountInfo = await mirrorNode.get(`/accounts/${txReceipt.from}`, requestDetails); expect(txReceipt.to).to.eq(accounts[2].address.toLowerCase()); expect(fromAccountInfo.evm_address).to.eq(accounts[1].address.toLowerCase()); diff --git a/packages/ws-server/tests/acceptance/subscribeNewHeads.spec.ts b/packages/ws-server/tests/acceptance/subscribeNewHeads.spec.ts index 5fc05c2862..8f4db50ab0 100644 --- a/packages/ws-server/tests/acceptance/subscribeNewHeads.spec.ts +++ b/packages/ws-server/tests/acceptance/subscribeNewHeads.spec.ts @@ -27,6 +27,8 @@ import { predefined } from '@hashgraph/json-rpc-relay'; import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils'; import Assertions from '@hashgraph/json-rpc-server/tests/helpers/assertions'; import { AliasAccount } from '@hashgraph/json-rpc-server/tests/types/AliasAccount'; +import MirrorClient from '@hashgraph/json-rpc-server/tests/clients/mirrorClient'; +import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient'; chai.use(solidity); const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`; @@ -103,7 +105,8 @@ describe('@web-socket-batch-3 eth_subscribe newHeads', async function () { before(async () => { // @ts-ignore - const { socketServer, mirrorNode, relay } = global; + const { socketServer, mirrorNode, relay }: { socketServer: any; mirrorNode: MirrorClient; relay: RelayClient } = + global; mirrorNodeServer = mirrorNode; rpcServer = relay; wsServer = socketServer;