diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 93535b5..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: NPM Semantic Release - -on: - push: - branches: ['main'] - -jobs: - Deployment: - name: Publish Chain Indexer Framework - runs-on: ubuntu-latest - env: - working-directory: ./ - permissions: - contents: read - packages: write - steps: - - name: CHECK-OUT GIT REPOSITORY - uses: actions/checkout@v3 - with: - persist-credentials: false - fetch-depth: 0 - - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: '18.x' - registry-url: 'https://npm.pkg.github.com' - - - name: Install dependencies - run: npm install - - - name: Build - run: npm run build - - - name: Run semantic-release - run: npx semantic-release - env: - GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} diff --git a/README.md b/README.md index 31657ae..35c8f0e 100644 --- a/README.md +++ b/README.md @@ -860,7 +860,7 @@ npm run build:link ## Support -Our [Discord](https://discord.gg/0xPolygonDevs) is the best way to reach us ✨. +Our [Discord](https://discord.com/invite/0xpolygonrnd) is the best way to reach us ✨. ## Contributing diff --git a/internal/block_getters/block_getter.ts b/internal/block_getters/block_getter.ts index 06c1652..9a0cfff 100644 --- a/internal/block_getters/block_getter.ts +++ b/internal/block_getters/block_getter.ts @@ -13,14 +13,14 @@ import { Logger } from "../logger/logger.js"; * * @author - Vibhu Rajeev, Nitin Mittal */ -export class BlockGetter extends BlockFormatter implements IBlockGetter { +export class BlockGetter extends BlockFormatter implements IBlockGetter { /** * @param {Eth} eth - Eth module from web3.js * @param {number} maxRetries - The number of times to retry on errors. * * @constructor */ - constructor(protected eth: Eth, protected maxRetries: number = 0) { + constructor(protected eth: Eth, protected maxRetries: number = 0) { super(); } @@ -46,25 +46,50 @@ export class BlockGetter extends BlockFormatter implements IBlockGetter { * * @throws {Error} - Throws error object on failure. */ - public async getBlockWithTransactionReceipts(blockNumber: number | string): Promise { - const block: BlockTransactionObject = await this.eth.getBlock(blockNumber, true); - Logger.debug(`Fetching transaction receipts for the following block ${block.number}`); + public async getBlockWithTransactionReceipts(blockNumber: number | string, errorCount: number = 0): Promise { + try { + const block: BlockTransactionObject = await this.eth.getBlock(blockNumber, true); + + if (!block) { + throw new BlockProducerError( + "Block producer error", + BlockProducerError.codes.RECEIPT_NOT_FOUND, + true, + `null receipt found for block ${blockNumber}.`, + "remote" + ); + } + + Logger.debug(`Fetching transaction receipts for the following block ${block.number}`); - const transactions: ITransaction[] = []; + const transactions: ITransaction[] = []; - for (const transactionObject of block.transactions) { - transactions.push( - this.formatTransactionObject( - transactionObject as IWeb3Transaction, - await this.getTransactionReceipt(transactionObject.hash) - ) + for (const transactionObject of block.transactions) { + transactions.push( + this.formatTransactionObject( + transactionObject as IWeb3Transaction, + await this.getTransactionReceipt(transactionObject.hash) + ) + ); + } + + return this.formatBlockWithTransactions( + block, + transactions ); - } + } catch (error) { + if (errorCount > this.maxRetries) { + Logger.info({ + location: "block_getter", + function: "getBlockWithTransactionReceipts", + errorCount, + error: JSON.stringify(error) + }); + throw error; + } - return this.formatBlockWithTransactions( - block, - transactions - ); + return await this.getBlockWithTransactionReceipts(blockNumber, errorCount + 1); + } } /** @@ -107,11 +132,17 @@ export class BlockGetter extends BlockFormatter implements IBlockGetter { return this.formatTransactionReceipt(transactionReceipt); } catch (error) { - if (errorCount >= this.maxRetries) { + if (errorCount > this.maxRetries) { + Logger.info({ + location: "block_getter", + function: "getTransactionReceipt", + errorCount, + error: JSON.stringify(error) + }); throw error; } - return this.getTransactionReceipt(transactionHash, errorCount + 1); + return await this.getTransactionReceipt(transactionHash, errorCount + 1); } } } diff --git a/internal/logger/logger.ts b/internal/logger/logger.ts index 938fa0c..1b201b8 100644 --- a/internal/logger/logger.ts +++ b/internal/logger/logger.ts @@ -7,67 +7,72 @@ let logger: WinstonLogger | null = null; /** * LoggerClass that maintains a singleton, and has straightforward methods to log any application events. - * + * * @author - Vibhu Rajeev, Keshav Gupta - Polygon Technology */ export class Logger { /** * @static - * Create method must first be called before using the logger. It creates a singleton, which will then - * be referred to throughout the application. - * + * Create method must first be called before using the logger. It creates a singleton, which will then + * be referred to throughout the application. + * * @param {LoggerConfig} config - Logger configuration to overwrite winston configs and define sentry + datadog endpoints. */ static create(config: LoggerConfig) { if (!logger) { - logger = winston.createLogger(Object.assign({ - format: winston.format.combine( - winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss:ms" }), - winston.format.colorize({ - all: true, - colors: { - error: "red", - warn: "yellow", - info: "green", - debug: "white", - } - }), - winston.format.printf( - (info: any) => `${info.timestamp} ${info.level}: ${info.message}`, - ), - ), - transports: [ - new winston.transports.Console({ - level: config.console?.level || "info" - }), - new Sentry( - { - sentry: { - dsn: config.sentry?.dsn, - environment: config.sentry?.environment || "development" - }, - level: config.sentry?.level || "error", - } - ), - new winston.transports.Http( - { - host: "http-intake.logs.datadoghq.com", - path: "/api/v2/logs?dd-api-key=" + config.datadog?.api_key + "&ddsource=nodejs&service=" + config.datadog?.service_name, - ssl: true - } - ), - ] - }, - config.winston - )); + logger = winston.createLogger( + Object.assign( + { + format: winston.format.combine( + winston.format.timestamp({ + format: "YYYY-MM-DD HH:mm:ss:ms", + }), + winston.format.colorize({ + all: true, + colors: { + error: "red", + warn: "yellow", + info: "green", + debug: "white", + }, + }), + winston.format.printf( + (info: any) => + `${info.timestamp} ${info.level}: ${info.message}` + ) + ), + transports: [ + new winston.transports.Console({ + level: config.console?.level || "info", + }), + new Sentry({ + sentry: { + dsn: config.sentry?.dsn, + }, + level: config.sentry?.level || "error", + }), + new winston.transports.Http({ + host: "http-intake.logs.datadoghq.com", + path: + "/api/v2/logs?dd-api-key=" + + config.datadog?.api_key + + "&ddsource=nodejs&service=" + + config.datadog?.service_name, + ssl: true, + }), + ], + }, + config.winston + ) + ); } } /** * @static - * Method to log for level - "info", this should not be called if it has been custom levels are + * Method to log for level - "info", this should not be called if it has been custom levels are * set which does not include "info" - * + * * @param {string|object} message - String or object to log. */ static info(message: string | object): void { @@ -80,9 +85,9 @@ export class Logger { /** * @static - * Method to log for level - "debug", this should not be called if it has been custom levels are + * Method to log for level - "debug", this should not be called if it has been custom levels are * set which does not include "debug" - * + * * @param {string|object} message - String or object to log. */ static debug(message: string | object): void { @@ -95,9 +100,9 @@ export class Logger { /** * @static - * Method to log for level - "error", this should not be called if it has been custom levels are + * Method to log for level - "error", this should not be called if it has been custom levels are * set which does not include "error" - * + * * @param {string|object} error - String or object to log. */ static error(error: string | object): void { @@ -105,16 +110,20 @@ export class Logger { logger?.error(error); } else { logger?.error( - `${(error as Error).message ? `${(error as Error).message} : ` : ""}${JSON.stringify(error)}` + `${ + (error as Error).message + ? `${(error as Error).message} : ` + : "" + }${JSON.stringify(error)}` ); } } /** * @static - * Method to log for level - "warn", this should not be called if it has been custom levels are + * Method to log for level - "warn", this should not be called if it has been custom levels are * set which does not include "warn" - * + * * @param {string|object} message - String or object to log. */ static warn(message: string | object): void { @@ -128,7 +137,7 @@ export class Logger { /** * @static * Method to log for any level, which should be used to log all custom levels that may be added. - * + * * @param {string|object} message - String or object to log. */ static log(level: string, message: string | object): void { diff --git a/package-lock.json b/package-lock.json index 7b8171f..69852ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@maticnetwork/chain-indexer-framework", - "version": "1.3.1", + "version": "1.3.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@maticnetwork/chain-indexer-framework", - "version": "1.3.1", + "version": "1.3.13", "license": "MIT", "dependencies": { "axios": "^1.3.6", - "ethereum-bloom-filters": "^1.0.10", + "ethereum-bloom-filters": "^1.2.0", "long": "^5.2.0", "mongoose": "^6.5.2", "node-rdkafka": "^2.13.0", @@ -8076,11 +8076,22 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/ethereum-bloom-filters": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", - "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.2.0.tgz", + "integrity": "sha512-28hyiE7HVsWubqhpVLVmZXFd4ITeHi+BUu05o9isf0GUpMtzBUi+8/gFrGaGYzvGAJQmJ3JKj77Mk9G98T84rA==", "dependencies": { - "js-sha3": "^0.8.0" + "@noble/hashes": "^1.4.0" + } + }, + "node_modules/ethereum-bloom-filters/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/ethereum-cryptography": { diff --git a/package.json b/package.json index fe6c1e9..d397e85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@maticnetwork/chain-indexer-framework", - "version": "1.3.9", + "version": "1.3.14", "description": "blockchain data indexer", "type": "module", "exports": { @@ -45,7 +45,7 @@ "homepage": "https://github.com/0xPolygon/chain-indexer-framework#readme", "dependencies": { "axios": "^1.3.6", - "ethereum-bloom-filters": "^1.0.10", + "ethereum-bloom-filters": "^1.2.0", "long": "^5.2.0", "mongoose": "^6.5.2", "node-rdkafka": "^2.13.0", diff --git a/tests/logger/logger.test.ts b/tests/logger/logger.test.ts index f502776..f4c29d1 100644 --- a/tests/logger/logger.test.ts +++ b/tests/logger/logger.test.ts @@ -22,10 +22,10 @@ jest.mock("winston", () => { }, transports: { Console: jest.fn().mockImplementation(() => { - return {console: true}; + return { console: true }; }), Http: jest.fn().mockImplementation(() => { - return {datadog: true}; + return { datadog: true }; }) }, createLogger: jest.fn() @@ -40,42 +40,42 @@ describe("Logger", () => { mockedSentry: jest.MockedObject; let mockedLogger: jest.MockedObject = { - info: jest.fn(), - debug: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - log: jest.fn() - } as jest.MockedObject; - - beforeAll(()=>{ - Logger.create({ + info: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + log: jest.fn() + } as jest.MockedObject; + + beforeAll(() => { + Logger.create({ winston: { level: "error" }, sentry: { - dsn: 'test_dsn', - level: 'error', + dsn: 'test_dsn', + level: 'error', environment: 'staging' - }, + }, datadog: { - api_key: 'test_api_key', - service_name: 'test_app_key' + api_key: 'test_api_key', + service_name: 'test_app_key' } }); }) - beforeEach(() => { + beforeEach(() => { mockedWinston = winston as jest.Mocked; mockedWinstonFormat = winston.format as jest.MockedObject; - mockedWinstonTransports = winston.transports; + mockedWinstonTransports = winston.transports; mockedSentryClass = Sentry as jest.MockedClass; }); test("create - should create logger with passed config or default config otherwise", () => { const colorizer = { addColors: (colors) => { } } as winston.Logform.Colorizer; - mockedWinstonFormat.combine.mockReturnValueOnce({format: true} as unknown as winston.Logform.Format); - + mockedWinstonFormat.combine.mockReturnValueOnce({ format: true } as unknown as winston.Logform.Format); + mockedWinston.createLogger.mockReturnValueOnce(mockedLogger); mockedWinstonFormat.timestamp.mockReturnValueOnce( @@ -86,23 +86,23 @@ describe("Logger", () => { {} as winston.Logform.Format ); - Logger.create({ + Logger.create({ winston: { level: "error" }, sentry: { - dsn: 'test_dsn', - level: 'error', + dsn: 'test_dsn', + level: 'error', environment: 'staging' - }, + }, datadog: { - api_key: 'test_api_key', - service_name: 'test_app_key' + api_key: 'test_api_key', + service_name: 'test_app_key' } }); mockedSentry = mockedSentryClass.mock.instances[0] as unknown as jest.MockedObject; - + expect(mockedWinstonFormat.combine).toHaveBeenNthCalledWith( 1, { options: {} }, @@ -116,11 +116,11 @@ describe("Logger", () => { expect(mockedWinston.createLogger).toHaveBeenCalledWith( { level: 'error', - format: {format: true}, + format: { format: true }, transports: [ - {console: true}, + { console: true }, mockedSentry, - {datadog: true} + { datadog: true } ] } ); @@ -152,8 +152,8 @@ describe("Logger", () => { { level: 'error', sentry: { - dsn: 'test_dsn', - environment: 'staging' + dsn: 'test_dsn', + // environment: 'staging' } } ); @@ -172,18 +172,18 @@ describe("Logger", () => { test('Logger is a singleton', () => { // Create an instance of Logger - Logger.create({ + Logger.create({ winston: { level: "error" }, sentry: { - dsn: 'test_dsn2', - level: 'error' , + dsn: 'test_dsn2', + level: 'error', environment: 'staging' - }, + }, datadog: { - api_key: 'test_api_key2', - service_name: 'test_app_key' + api_key: 'test_api_key2', + service_name: 'test_app_key' } }); @@ -191,7 +191,7 @@ describe("Logger", () => { //calling Logger create method will not call the createLogger of mockedWinston. Verifying this will serve as our test for Singleton. expect(mockedWinston.createLogger).toHaveBeenCalledTimes(0); }); - + test("info must call logger.info with the message passed", () => { Logger.info("mock");