From b67dc8cd4e21f91a50a1c0c85880d2f265ab1d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A1rdenas?= Date: Tue, 22 Oct 2024 08:43:19 -0600 Subject: [PATCH] fix: upgrade to new chainhook ts client (#280) --- package-lock.json | 8 +-- package.json | 2 +- src/chainhook/server.ts | 57 +++-------------- tests/chainhook/predicates.test.ts | 99 ------------------------------ 4 files changed, 15 insertions(+), 151 deletions(-) delete mode 100644 tests/chainhook/predicates.test.ts diff --git a/package-lock.json b/package-lock.json index 48d47cdf..8354c4b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@fastify/type-provider-typebox": "^3.2.0", "@google-cloud/storage": "^7.12.1", "@hirosystems/api-toolkit": "^1.7.1", - "@hirosystems/chainhook-client": "^1.12.0", + "@hirosystems/chainhook-client": "^2.0.0", "@sinclair/typebox": "^0.28.17", "@stacks/transactions": "^6.1.0", "@types/node": "^20.16.1", @@ -2700,9 +2700,9 @@ } }, "node_modules/@hirosystems/chainhook-client": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.12.0.tgz", - "integrity": "sha512-FUlYMjnM2CGkxuBR0r+8+HPj+fhpJBJdcuS2e9YFz1NXfE7aDwM4bB5IxlcsJA2a5YAge1tZWeJUdR+TAnv/Rg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-2.0.0.tgz", + "integrity": "sha512-CZrByDwTr4Re5PBSlr7spHCRcdZLVf4D/wmuPmMdmJjreOohcwOBmSxKQG6kwMHbnBtcVXoI9rn1c96GoaNf6Q==", "dependencies": { "@fastify/type-provider-typebox": "^3.2.0", "fastify": "^4.15.0", diff --git a/package.json b/package.json index c81d52fa..a7f06312 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@fastify/type-provider-typebox": "^3.2.0", "@google-cloud/storage": "^7.12.1", "@hirosystems/api-toolkit": "^1.7.1", - "@hirosystems/chainhook-client": "^1.12.0", + "@hirosystems/chainhook-client": "^2.0.0", "@sinclair/typebox": "^0.28.17", "@stacks/transactions": "^6.1.0", "@types/node": "^20.16.1", diff --git a/src/chainhook/server.ts b/src/chainhook/server.ts index 570efd02..f66a79b1 100644 --- a/src/chainhook/server.ts +++ b/src/chainhook/server.ts @@ -1,55 +1,23 @@ -import * as fs from 'fs'; import { ChainhookEventObserver, ChainhookNodeOptions, + EventObserverOptions, + EventObserverPredicate, Payload, - ServerOptions, - ServerPredicate, StacksPayload, } from '@hirosystems/chainhook-client'; import { PgStore } from '../pg/pg-store'; import { ENV } from '../env'; import { logger } from '@hirosystems/api-toolkit'; -import { randomUUID } from 'node:crypto'; - -export function getPersistedPredicateFromDisk(): ServerPredicate | undefined { - const predicatePath = `${ENV.CHAINHOOK_PREDICATE_PATH}/predicate.json`; - try { - if (!fs.existsSync(predicatePath)) { - return; - } - const fileData = fs.readFileSync(predicatePath, 'utf-8'); - return JSON.parse(fileData) as ServerPredicate; - } catch (error) { - logger.error(error, `ChainhookServer unable to get persisted predicate`); - } -} - -export function persistPredicateToDisk(predicate: ServerPredicate) { - const predicatePath = `${ENV.CHAINHOOK_PREDICATE_PATH}/predicate.json`; - try { - fs.mkdirSync(ENV.CHAINHOOK_PREDICATE_PATH, { recursive: true }); - fs.writeFileSync(predicatePath, JSON.stringify(predicate, null, 2)); - } catch (error) { - logger.error(error, `ChainhookServer unable to persist predicate to disk`); - } -} export async function startChainhookServer(args: { db: PgStore }): Promise { const blockHeight = await args.db.getChainTipBlockHeight(); logger.info(`ChainhookServer is at block ${blockHeight}`); - const predicates: ServerPredicate[] = []; + const predicates: EventObserverPredicate[] = []; if (ENV.CHAINHOOK_AUTO_PREDICATE_REGISTRATION) { - const existingPredicate = getPersistedPredicateFromDisk(); - if (existingPredicate) { - logger.info( - `ChainhookServer will attempt to resume existing predicate ${existingPredicate.uuid}` - ); - } const header = { - uuid: existingPredicate?.uuid ?? randomUUID(), - name: 'block', + name: 'metadata-api-blocks', version: 1, chain: 'stacks', }; @@ -87,7 +55,7 @@ export async function startChainhookServer(args: { db: PgStore }): Promise { + const server = new ChainhookEventObserver(observer, chainhook); + await server.start(predicates, async (payload: Payload) => { logger.info( `ChainhookServer received ${ payload.chainhook.is_streaming_blocks ? 'streamed' : 'replay' - } payload from predicate ${uuid}` + } payload from predicate ${payload.chainhook.uuid}` ); await args.db.chainhook.processPayload(payload as StacksPayload); }); - if (predicates.length) persistPredicateToDisk(predicates[0]); return server; } export async function closeChainhookServer(server: ChainhookEventObserver) { - try { - const predicatePath = `${ENV.CHAINHOOK_PREDICATE_PATH}/predicate.json`; - if (fs.existsSync(predicatePath)) fs.rmSync(predicatePath); - } catch (error) { - logger.error(error, `ChainhookServer unable to delete persisted predicate`); - } await server.close(); } diff --git a/tests/chainhook/predicates.test.ts b/tests/chainhook/predicates.test.ts deleted file mode 100644 index f0374af2..00000000 --- a/tests/chainhook/predicates.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { Interceptable, MockAgent, setGlobalDispatcher } from 'undici'; -import { ENV } from '../../src/env'; -import { - closeChainhookServer, - getPersistedPredicateFromDisk, - persistPredicateToDisk, - startChainhookServer, -} from '../../src/chainhook/server'; -import { MIGRATIONS_DIR, PgStore } from '../../src/pg/pg-store'; -import { cycleMigrations } from '@hirosystems/api-toolkit'; -import { ChainhookEventObserver } from '@hirosystems/chainhook-client'; - -describe('predicates', () => { - let db: PgStore; - let mockAgent: MockAgent; - let mockClient: Interceptable; - let server: ChainhookEventObserver; - - beforeAll(async () => { - ENV.CHAINHOOK_PREDICATE_PATH = './tmp'; - ENV.CHAINHOOK_AUTO_PREDICATE_REGISTRATION = true; - ENV.PGDATABASE = 'postgres'; - db = await PgStore.connect({ skipMigrations: true }); - await cycleMigrations(MIGRATIONS_DIR); - }); - - afterAll(async () => { - await db.close(); - }); - - beforeEach(() => { - mockAgent = new MockAgent(); - mockAgent.disableNetConnect(); - mockClient = mockAgent.get('http://127.0.0.1:20456'); - mockClient - .intercept({ - path: '/ping', - method: 'GET', - }) - .reply(200); - setGlobalDispatcher(mockAgent); - }); - - afterEach(async () => { - mockClient - .intercept({ - path: /\/v1\/chainhooks\/stacks\/(.*)/, - method: 'DELETE', - }) - .reply(200); - await closeChainhookServer(server); - await mockAgent.close(); - }); - - test('registers and persists new predicate to disk', async () => { - mockClient - .intercept({ - path: /\/v1\/chainhooks\/(.*)/, - method: 'GET', - }) - .reply(200, { status: 404 }); // New predicate - mockClient - .intercept({ - path: '/v1/chainhooks', - method: 'POST', - }) - .reply(200); - server = await startChainhookServer({ db }); - expect(getPersistedPredicateFromDisk()).not.toBeUndefined(); - mockAgent.assertNoPendingInterceptors(); - }); - - test('resumes predicate stored on disk', async () => { - persistPredicateToDisk({ - uuid: 'e2777d77-473a-4c1d-9012-152deb36bf4c', - name: 'test', - version: 1, - chain: 'stacks', - networks: { - mainnet: { - start_block: 1, - include_contract_abi: true, - if_this: { - scope: 'block_height', - higher_than: 1, - }, - }, - }, - }); - mockClient - .intercept({ - path: '/v1/chainhooks/e2777d77-473a-4c1d-9012-152deb36bf4c', - method: 'GET', - }) - .reply(200, { result: { enabled: true, status: { type: 'scanning' } }, status: 200 }); - server = await startChainhookServer({ db }); - mockAgent.assertNoPendingInterceptors(); - }); -});