diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35853d17..72828daa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,15 +18,15 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Use Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' - name: Cache node modules - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-node-modules with: @@ -63,17 +63,17 @@ jobs: PGPASSWORD: postgres PGDATABASE: postgres steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Use Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' - name: Cache node modules - uses: actions/cache@v2 + uses: actions/cache@v4 env: cache-name: cache-node-modules with: @@ -99,7 +99,9 @@ jobs: run: npm run test:${{ matrix.suite }} -- --coverage - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} - name: Print integration environment logs run: cat docker-compose-logs.txt @@ -115,14 +117,14 @@ jobs: - lint - test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: token: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} fetch-depth: 0 persist-credentials: false - name: Semantic Release - uses: cycjimmy/semantic-release-action@v3 + uses: cycjimmy/semantic-release-action@v4 id: semantic # Only run on non-PR events or only PRs that aren't from forks if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository @@ -137,11 +139,11 @@ jobs: conventional-changelog-conventionalcommits@6.1.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Docker Meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: images: | hirosystems/${{ github.event.repository.name }} @@ -152,13 +154,13 @@ jobs: type=semver,pattern={{major}}.{{minor}},value=${{ steps.semantic.outputs.new_release_version }},enable=${{ steps.semantic.outputs.new_release_version != '' }} - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: Build/Tag/Push Image - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v5 with: context: . tags: ${{ steps.meta.outputs.tags }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7444d9bc..24ccb29d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,64 @@ +## [3.0.0-beta.2](https://github.com/hirosystems/ordinals-api/compare/v3.0.0-beta.1...v3.0.0-beta.2) (2024-02-21) + + +### Features + +* shutdown gracefully after finishing replaying blocks ([#315](https://github.com/hirosystems/ordinals-api/issues/315)) ([72fd3fd](https://github.com/hirosystems/ordinals-api/commit/72fd3fda24919f14218cbbf02a552dfcf4d8ea52)) + +## [3.0.0-beta.1](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.6...v3.0.0-beta.1) (2024-02-21) + + +### ⚠ BREAKING CHANGES + +* rename chainhook env vars to ordhook (#314) + +### Bug Fixes + +* rename chainhook env vars to ordhook ([#314](https://github.com/hirosystems/ordinals-api/issues/314)) ([ae4ec01](https://github.com/hirosystems/ordinals-api/commit/ae4ec01326779af00b89907751a6946effe60536)) + +## [2.3.0-beta.6](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.5...v2.3.0-beta.6) (2024-02-20) + + +### Bug Fixes + +* remove unused pg indexes ([#311](https://github.com/hirosystems/ordinals-api/issues/311)) ([94d98d4](https://github.com/hirosystems/ordinals-api/commit/94d98d4e945aef28be7ec19fdb07d121c0ba2ea2)) + +## [2.3.0-beta.5](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.4...v2.3.0-beta.5) (2024-02-19) + + +### Bug Fixes + +* only update timestamp when streaming blocks ([#309](https://github.com/hirosystems/ordinals-api/issues/309)) ([2c9ff17](https://github.com/hirosystems/ordinals-api/commit/2c9ff17ebe8805f3de59046b25387b871debee49)) + +## [2.3.0-beta.4](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.3...v2.3.0-beta.4) (2024-02-17) + + +### Bug Fixes + +* standardize insert batch size ([b9b2448](https://github.com/hirosystems/ordinals-api/commit/b9b2448a1fa1bef16f0a1eed80158ca9b8ca9133)) + +## [2.3.0-beta.3](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.2...v2.3.0-beta.3) (2024-02-17) + + +### Bug Fixes + +* batch inscription location updates ([47525c9](https://github.com/hirosystems/ordinals-api/commit/47525c93a0e71bd9a7df2ea1596d7c90f59ccaac)) + +## [2.3.0-beta.2](https://github.com/hirosystems/ordinals-api/compare/v2.3.0-beta.1...v2.3.0-beta.2) (2024-02-17) + + +### Bug Fixes + +* batch location pointer inserts ([#308](https://github.com/hirosystems/ordinals-api/issues/308)) ([33f8cb2](https://github.com/hirosystems/ordinals-api/commit/33f8cb2576115695d8e54fcc989086afef42ddc7)) + +## [2.3.0-beta.1](https://github.com/hirosystems/ordinals-api/compare/v2.2.0...v2.3.0-beta.1) (2024-02-12) + + +### Features + +* add `metadata` and `parent` columns to inscriptions table ([#305](https://github.com/hirosystems/ordinals-api/issues/305)) ([d71e93a](https://github.com/hirosystems/ordinals-api/commit/d71e93a54ec8018c15bbfc2f88d18bab6606949e)) +* add ordhook debug server ([#306](https://github.com/hirosystems/ordinals-api/issues/306)) ([88ad130](https://github.com/hirosystems/ordinals-api/commit/88ad1302924b8da27b8d98caf3d3351149a45f91)) + ## [2.2.0](https://github.com/hirosystems/ordinals-api/compare/v2.1.1...v2.2.0) (2024-02-02) diff --git a/migrations/1707770109739_metadata-parent.ts b/migrations/1707770109739_metadata-parent.ts new file mode 100644 index 00000000..0c33c976 --- /dev/null +++ b/migrations/1707770109739_metadata-parent.ts @@ -0,0 +1,15 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'; + +export const shorthands: ColumnDefinitions | undefined = undefined; + +export function up(pgm: MigrationBuilder): void { + pgm.addColumns('inscriptions', { + metadata: { + type: 'text', + }, + parent: { + type: 'text', + }, + }); +} diff --git a/migrations/1708471015438_remove-unused-indexes.ts b/migrations/1708471015438_remove-unused-indexes.ts new file mode 100644 index 00000000..2ba978b7 --- /dev/null +++ b/migrations/1708471015438_remove-unused-indexes.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { MigrationBuilder, ColumnDefinitions } from 'node-pg-migrate'; + +export const shorthands: ColumnDefinitions | undefined = undefined; + +export function up(pgm: MigrationBuilder): void { + pgm.dropIndex('locations', ['prev_output']); + pgm.dropIndex('locations', ['address']); + pgm.dropIndex('current_locations', ['block_height']); + pgm.dropIndex('brc20_mints', ['address']); + pgm.dropIndex('brc20_mints', ['block_height']); + pgm.dropIndex('brc20_mints', ['brc20_deploy_id']); + pgm.dropIndex('brc20_transfers', ['to_address']); + pgm.dropIndex('brc20_transfers', ['from_address']); + pgm.dropIndex('brc20_transfers', ['brc20_deploy_id']); + pgm.dropIndex('brc20_transfers', ['block_height']); + pgm.dropIndex('brc20_deploys', ['address']); + pgm.dropIndex('brc20_deploys', ['block_height']); + pgm.dropIndex('inscription_recursions', ['ref_inscription_genesis_id']); +} + +export function down(pgm: MigrationBuilder): void { + pgm.createIndex('locations', ['prev_output']); + pgm.createIndex('locations', ['address']); + pgm.createIndex('current_locations', ['block_height']); + pgm.createIndex('brc20_mints', ['address']); + pgm.createIndex('brc20_mints', ['block_height']); + pgm.createIndex('brc20_mints', ['brc20_deploy_id']); + pgm.createIndex('brc20_transfers', ['to_address']); + pgm.createIndex('brc20_transfers', ['from_address']); + pgm.createIndex('brc20_transfers', ['brc20_deploy_id']); + pgm.createIndex('brc20_transfers', ['block_height']); + pgm.createIndex('brc20_deploys', ['address']); + pgm.createIndex('brc20_deploys', ['block_height']); + pgm.createIndex('inscription_recursions', ['ref_inscription_genesis_id']); +} diff --git a/package-lock.json b/package-lock.json index 5681507e..28f91c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,8 @@ "@fastify/multipart": "^7.1.0", "@fastify/swagger": "^8.3.1", "@fastify/type-provider-typebox": "^3.2.0", - "@hirosystems/api-toolkit": "^1.3.1", - "@hirosystems/chainhook-client": "^1.6.0", + "@hirosystems/api-toolkit": "^1.4.0", + "@hirosystems/chainhook-client": "^1.7.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", @@ -1278,9 +1278,9 @@ } }, "node_modules/@hirosystems/api-toolkit": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@hirosystems/api-toolkit/-/api-toolkit-1.3.1.tgz", - "integrity": "sha512-uUOqWcJlaxnlW30RyZ1UdidzFy29esd4bG0UxwnsJH+M+qtvV4V/NaHLRUFbnJkoF5b6vckZh1mldyBNY7aL1Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@hirosystems/api-toolkit/-/api-toolkit-1.4.0.tgz", + "integrity": "sha512-n1LF5roEQ7LkfAvKw0Wucmbo+XhvQBp4ED9N/AyD76/wHQeU59nocDkVAoKSJzWzzCfHa2+G32d3zB3m6oMbIQ==", "dependencies": { "@fastify/cors": "^8.0.0", "@fastify/swagger": "^8.3.1", @@ -1299,9 +1299,9 @@ } }, "node_modules/@hirosystems/chainhook-client": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.6.0.tgz", - "integrity": "sha512-8skXF0Hk5XL5LH6enYySyuwEy3Kzn8AAb5+ERG0w9WSfehBNjLkL3zKJFuwg8Ov926YzE7a2ZnzlUpVmvvlulw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.7.0.tgz", + "integrity": "sha512-XRSbpu+Bxwvd8qqQTNcomfO8RYu+Dpnl9ZnB8EJE+tvJ4y3lUZD6Uk65368Us0Hbw+VNWnU2ibej7iqB6mGsOA==", "dependencies": { "@fastify/type-provider-typebox": "^3.2.0", "fastify": "^4.15.0", @@ -19728,9 +19728,9 @@ "requires": {} }, "@hirosystems/api-toolkit": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@hirosystems/api-toolkit/-/api-toolkit-1.3.1.tgz", - "integrity": "sha512-uUOqWcJlaxnlW30RyZ1UdidzFy29esd4bG0UxwnsJH+M+qtvV4V/NaHLRUFbnJkoF5b6vckZh1mldyBNY7aL1Q==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@hirosystems/api-toolkit/-/api-toolkit-1.4.0.tgz", + "integrity": "sha512-n1LF5roEQ7LkfAvKw0Wucmbo+XhvQBp4ED9N/AyD76/wHQeU59nocDkVAoKSJzWzzCfHa2+G32d3zB3m6oMbIQ==", "requires": { "@fastify/cors": "^8.0.0", "@fastify/swagger": "^8.3.1", @@ -19743,9 +19743,9 @@ } }, "@hirosystems/chainhook-client": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.6.0.tgz", - "integrity": "sha512-8skXF0Hk5XL5LH6enYySyuwEy3Kzn8AAb5+ERG0w9WSfehBNjLkL3zKJFuwg8Ov926YzE7a2ZnzlUpVmvvlulw==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@hirosystems/chainhook-client/-/chainhook-client-1.7.0.tgz", + "integrity": "sha512-XRSbpu+Bxwvd8qqQTNcomfO8RYu+Dpnl9ZnB8EJE+tvJ4y3lUZD6Uk65368Us0Hbw+VNWnU2ibej7iqB6mGsOA==", "requires": { "@fastify/type-provider-typebox": "^3.2.0", "fastify": "^4.15.0", diff --git a/package.json b/package.json index 61b642a5..08d55d38 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "rimraf ./dist && tsc --project tsconfig.build.json", "start": "node dist/src/index.js", "start-ts": "ts-node ./src/index.ts", + "start:debug-server": "node dist/util/debug-server.js", "test": "jest --runInBand", "test:brc-20": "npm run test -- ./tests/brc-20/", "test:api": "npm run test -- ./tests/api/", @@ -53,8 +54,8 @@ "@fastify/multipart": "^7.1.0", "@fastify/swagger": "^8.3.1", "@fastify/type-provider-typebox": "^3.2.0", - "@hirosystems/api-toolkit": "^1.3.1", - "@hirosystems/chainhook-client": "^1.6.0", + "@hirosystems/api-toolkit": "^1.4.0", + "@hirosystems/chainhook-client": "^1.7.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^10.0.4", "@semantic-release/git": "^10.0.1", diff --git a/src/env.ts b/src/env.ts index 88f64f65..21aeef65 100644 --- a/src/env.ts +++ b/src/env.ts @@ -28,19 +28,31 @@ const schema = Type.Object({ EXTERNAL_HOSTNAME: Type.String({ default: '127.0.0.1' }), /** Hostname of the ordhook node we'll use to register predicates */ - CHAINHOOK_NODE_RPC_HOST: Type.String({ default: '127.0.0.1' }), + ORDHOOK_NODE_RPC_HOST: Type.String({ default: '127.0.0.1' }), /** Control port of the ordhook node */ - CHAINHOOK_NODE_RPC_PORT: Type.Number({ default: 20456, minimum: 0, maximum: 65535 }), + ORDHOOK_NODE_RPC_PORT: Type.Number({ default: 20456, minimum: 0, maximum: 65535 }), /** * Authorization token that the ordhook node must send with every event to make sure it's * coming from the valid instance */ - CHAINHOOK_NODE_AUTH_TOKEN: Type.String(), + ORDHOOK_NODE_AUTH_TOKEN: Type.String(), /** * Register ordhook predicates automatically when the API is first launched. Set this to `false` * if you're configuring your predicates manually for any reason. */ - CHAINHOOK_AUTO_PREDICATE_REGISTRATION: Type.Boolean({ default: true }), + ORDHOOK_AUTO_PREDICATE_REGISTRATION: Type.Boolean({ default: true }), + /** + * Ordhook ingestion mode. Controls the API's Ordhook payload ingestion behavior: + * * `default`: The API will stay running and will listen for payloads indefinitely + * * `replay`: The API will stay running and listening only for payloads marked as "not streaming" + * by Ordhook (historical replays). Once Ordhook starts streaming recent blocks from its chain + * tip, the API will shut down. Recommended for deployments meant to sync the ordinals chain + * from genesis. + */ + ORDHOOK_INGESTION_MODE: Type.Enum( + { default: 'default', replay: 'replay' }, + { default: 'default' } + ), PGHOST: Type.String(), PGPORT: Type.Number({ default: 5432, minimum: 0, maximum: 65535 }), diff --git a/src/ordhook/server.ts b/src/ordhook/server.ts index b7fae7b0..a414da32 100644 --- a/src/ordhook/server.ts +++ b/src/ordhook/server.ts @@ -8,9 +8,9 @@ import { ServerOptions, ServerPredicate, } from '@hirosystems/chainhook-client'; -import { logger } from '@hirosystems/api-toolkit'; +import { logger, shutdown } from '@hirosystems/api-toolkit'; -export const ORDHOOK_BASE_PATH = `http://${ENV.CHAINHOOK_NODE_RPC_HOST}:${ENV.CHAINHOOK_NODE_RPC_PORT}`; +export const ORDHOOK_BASE_PATH = `http://${ENV.ORDHOOK_NODE_RPC_HOST}:${ENV.ORDHOOK_NODE_RPC_PORT}`; export const PREDICATE_UUID = randomUUID(); /** @@ -20,7 +20,7 @@ export const PREDICATE_UUID = randomUUID(); */ export async function startOrdhookServer(args: { db: PgStore }): Promise { const predicates: ServerPredicate[] = []; - if (ENV.CHAINHOOK_AUTO_PREDICATE_REGISTRATION) { + if (ENV.ORDHOOK_AUTO_PREDICATE_REGISTRATION) { const blockHeight = await args.db.getChainTipBlockHeight(); logger.info(`Ordinals predicate starting from block ${blockHeight}...`); predicates.push({ @@ -43,9 +43,9 @@ export async function startOrdhookServer(args: { db: PgStore }): Promise { - logger.info(`OrdhookServer received payload from predicate ${uuid}`); + const streamed = payload.chainhook.is_streaming_blocks; + if (ENV.ORDHOOK_INGESTION_MODE === 'replay' && streamed) { + logger.info(`OrdhookServer finished replaying blocks, shutting down`); + return shutdown(); + } + logger.info( + `OrdhookServer received ${streamed ? 'streamed' : 'replay'} payload from predicate ${uuid}` + ); await args.db.updateInscriptions(payload); }); return server; diff --git a/src/pg/helpers.ts b/src/pg/helpers.ts index 4cef0600..25d5d504 100644 --- a/src/pg/helpers.ts +++ b/src/pg/helpers.ts @@ -114,6 +114,8 @@ function updateFromOrdhookInscriptionRevealed(args: { sat_rarity: satoshi.rarity, sat_coinbase_height: satoshi.blockHeight, recursive: recursive_refs.length > 0, + metadata: args.reveal.metadata ? JSON.stringify(args.reveal.metadata) : null, + parent: args.reveal.parent, }, location: { block_hash: args.block_hash, diff --git a/src/pg/pg-store.ts b/src/pg/pg-store.ts index ce4032be..c392ead9 100644 --- a/src/pg/pg-store.ts +++ b/src/pg/pg-store.ts @@ -26,10 +26,8 @@ import { DbInscriptionIndexFilters, DbInscriptionIndexOrder, DbInscriptionIndexPaging, - InscriptionData, DbInscriptionLocationChange, DbLocation, - RevealLocationData, DbLocationPointer, DbLocationPointerInsert, DbPaginatedResult, @@ -43,6 +41,7 @@ import { export const MIGRATIONS_DIR = path.join(__dirname, '../../migrations'); export const ORDINALS_GENESIS_BLOCK = 767430; +const INSERT_BATCH_SIZE = 4000; type InscriptionIdentifier = { genesis_id: string } | { number: number }; @@ -135,8 +134,8 @@ export class PgStore extends BasePgStore { currentBlockHeight: currentBlockHeight, newBlockHeight: event.block_identifier.index, }); - for (const writeChunk of batchIterate(writes, 4000)) - await this.insertInscriptions(writeChunk); + for (const writeChunk of batchIterate(writes, INSERT_BATCH_SIZE)) + await this.insertInscriptions(writeChunk, payload.chainhook.is_streaming_blocks); updatedBlockHeightMin = Math.min(updatedBlockHeightMin, event.block_identifier.index); logger.info( `PgStore ingested block ${event.block_identifier.index} in ${time.getElapsedSeconds()}s` @@ -488,7 +487,10 @@ export class PgStore extends BasePgStore { `; // roughly 35 days of blocks, assuming 10 minute block times on a full database } - private async insertInscriptions(reveals: InscriptionEventData[]): Promise { + private async insertInscriptions( + reveals: InscriptionEventData[], + streamed: boolean + ): Promise { if (reveals.length === 0) return; await this.sqlWriteTransaction(async sql => { const inscriptionInserts: InscriptionInsert[] = []; @@ -549,27 +551,31 @@ export class PgStore extends BasePgStore { sat_coinbase_height = EXCLUDED.sat_coinbase_height, updated_at = NOW() `; - const pointers = await sql` - INSERT INTO locations ${sql(locationInserts)} - ON CONFLICT ON CONSTRAINT locations_inscription_id_block_height_tx_index_unique DO UPDATE SET - genesis_id = EXCLUDED.genesis_id, - block_hash = EXCLUDED.block_hash, - tx_id = EXCLUDED.tx_id, - address = EXCLUDED.address, - value = EXCLUDED.value, - output = EXCLUDED.output, - "offset" = EXCLUDED.offset, - timestamp = EXCLUDED.timestamp - RETURNING inscription_id, id AS location_id, block_height, tx_index, address - `; + const pointers: DbLocationPointerInsert[] = []; + for (const batch of batchIterate(locationInserts, INSERT_BATCH_SIZE)) { + const pointerBatch = await sql` + INSERT INTO locations ${sql(batch)} + ON CONFLICT ON CONSTRAINT locations_inscription_id_block_height_tx_index_unique DO UPDATE SET + genesis_id = EXCLUDED.genesis_id, + block_hash = EXCLUDED.block_hash, + tx_id = EXCLUDED.tx_id, + address = EXCLUDED.address, + value = EXCLUDED.value, + output = EXCLUDED.output, + "offset" = EXCLUDED.offset, + timestamp = EXCLUDED.timestamp + RETURNING inscription_id, id AS location_id, block_height, tx_index, address + `; + await this.updateInscriptionLocationPointers(pointerBatch); + pointers.push(...pointerBatch); + } await this.updateInscriptionRecursions(reveals); - if (transferredOrdinalNumbers.length) + if (streamed && transferredOrdinalNumbers.length) await sql` UPDATE inscriptions SET updated_at = NOW() WHERE sat_ordinal IN ${sql(transferredOrdinalNumbers)} `; - await this.updateInscriptionLocationPointers(pointers); for (const reveal of reveals) { const action = 'inscription' in reveal diff --git a/src/pg/types.ts b/src/pg/types.ts index d070c91d..46680b91 100644 --- a/src/pg/types.ts +++ b/src/pg/types.ts @@ -20,6 +20,8 @@ export type InscriptionData = { sat_rarity: string; sat_coinbase_height: number; recursive: boolean; + metadata: string | null; + parent: string | null; }; export type InscriptionInsert = InscriptionData; diff --git a/tests/api/cache.test.ts b/tests/api/cache.test.ts index da974352..9eca102e 100644 --- a/tests/api/cache.test.ts +++ b/tests/api/cache.test.ts @@ -45,6 +45,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block); @@ -180,6 +185,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block1); @@ -208,6 +218,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block2); @@ -283,6 +298,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block1); @@ -330,6 +350,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block2); @@ -369,6 +394,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block1); @@ -416,6 +446,11 @@ describe('ETag cache', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(block2); diff --git a/tests/api/inscriptions.test.ts b/tests/api/inscriptions.test.ts index 0dbe1158..a9693510 100644 --- a/tests/api/inscriptions.test.ts +++ b/tests/api/inscriptions.test.ts @@ -54,6 +54,11 @@ describe('/inscriptions', () => { inscription_input_index: 0, transfers_pre_inscription: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -86,6 +91,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -163,6 +173,11 @@ describe('/inscriptions', () => { inscription_input_index: 0, transfers_pre_inscription: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .transaction({ hash: '0xf351d86c6e6cae3c64e297e7463095732f216875bcc1f3c03f950a492bb25421', @@ -185,6 +200,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -223,6 +243,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -303,6 +328,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -380,6 +410,11 @@ describe('/inscriptions', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -457,6 +492,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -602,6 +642,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -707,6 +752,11 @@ describe('/inscriptions', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -854,6 +904,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1018,6 +1073,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .transaction({ hash: '7ac73ecd01b9da4a7eab904655416dbfe8e03f193e091761b5a63ad0963570cd', @@ -1040,6 +1100,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 1, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1370,6 +1435,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1402,6 +1472,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1500,6 +1575,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1532,6 +1612,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1647,6 +1732,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1679,6 +1769,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1740,6 +1835,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1772,6 +1872,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1837,6 +1942,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1869,6 +1979,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1930,6 +2045,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -1962,6 +2082,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2025,6 +2150,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2070,6 +2200,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2102,6 +2237,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2149,6 +2289,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2181,6 +2326,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2236,6 +2386,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2268,6 +2423,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2323,6 +2483,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2355,6 +2520,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2410,6 +2580,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2442,6 +2617,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2496,6 +2676,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2528,6 +2713,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2563,6 +2753,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }; await db.updateInscriptions( new TestChainhookPayloadBuilder() @@ -2607,6 +2802,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2766,6 +2966,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2800,6 +3005,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2859,6 +3069,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2893,6 +3108,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: 'test', + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2952,6 +3172,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -2983,6 +3208,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(genesis2); @@ -3052,6 +3282,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3084,6 +3319,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3116,6 +3356,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3173,6 +3418,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3205,6 +3455,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3237,6 +3492,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3294,6 +3554,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3326,6 +3591,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3358,6 +3628,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3415,6 +3690,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3447,6 +3727,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -3479,6 +3764,11 @@ describe('/inscriptions', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); diff --git a/tests/api/sats.test.ts b/tests/api/sats.test.ts index 91b36b20..4747237a 100644 --- a/tests/api/sats.test.ts +++ b/tests/api/sats.test.ts @@ -63,6 +63,11 @@ describe('/sats', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -100,6 +105,11 @@ describe('/sats', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -132,6 +142,11 @@ describe('/sats', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); diff --git a/tests/api/stats.test.ts b/tests/api/stats.test.ts index 9215ee74..5f2b4c35 100644 --- a/tests/api/stats.test.ts +++ b/tests/api/stats.test.ts @@ -224,6 +224,11 @@ function testRevealApply(blockHeight: number, numbers: number[]) { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }); } return block.build(); diff --git a/tests/api/status.test.ts b/tests/api/status.test.ts index 2c79dc72..2cbabd39 100644 --- a/tests/api/status.test.ts +++ b/tests/api/status.test.ts @@ -55,6 +55,11 @@ describe('Status', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, curse_type: null, }) .build() @@ -82,6 +87,11 @@ describe('Status', () => { inscription_input_index: 0, transfers_pre_inscription: 0, tx_index: 0, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); diff --git a/tests/brc-20/brc20.test.ts b/tests/brc-20/brc20.test.ts index 3752d4d7..9f5a8608 100644 --- a/tests/brc-20/brc20.test.ts +++ b/tests/brc-20/brc20.test.ts @@ -102,6 +102,8 @@ describe('BRC-20', () => { sat_rarity: 'common', sat_coinbase_height: 110, recursive: false, + metadata: null, + parent: null, }; return insert; }; @@ -130,6 +132,8 @@ describe('BRC-20', () => { sat_rarity: 'common', sat_coinbase_height: 110, recursive: false, + metadata: null, + parent: null, }; expect(brc20FromInscription(insert)).toBeUndefined(); insert.content_type = 'application/json'; @@ -159,6 +163,8 @@ describe('BRC-20', () => { sat_rarity: 'common', sat_coinbase_height: 110, recursive: false, + metadata: null, + parent: null, }; expect(brc20FromInscription(insert)).toBeUndefined(); }); diff --git a/tests/helpers.ts b/tests/helpers.ts index e1f0087c..f26f86b3 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -137,6 +137,11 @@ export function brc20Reveal(args: { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: undefined, + parent: null, }; return reveal; } diff --git a/tests/ordhook/replay.test.ts b/tests/ordhook/replay.test.ts new file mode 100644 index 00000000..1e07d625 --- /dev/null +++ b/tests/ordhook/replay.test.ts @@ -0,0 +1,78 @@ +import { runMigrations } from '@hirosystems/api-toolkit'; +import { ChainhookEventObserver } from '@hirosystems/chainhook-client'; +import { buildApiServer } from '../../src/api/init'; +import { ENV } from '../../src/env'; +import { startOrdhookServer } from '../../src/ordhook/server'; +import { PgStore, MIGRATIONS_DIR } from '../../src/pg/pg-store'; +import { TestChainhookPayloadBuilder, TestFastifyServer } from '../helpers'; + +describe('Replay', () => { + let db: PgStore; + let server: ChainhookEventObserver; + let fastify: TestFastifyServer; + + beforeEach(async () => { + await runMigrations(MIGRATIONS_DIR, 'up'); + ENV.ORDHOOK_AUTO_PREDICATE_REGISTRATION = false; + ENV.ORDHOOK_INGESTION_MODE = 'replay'; + db = await PgStore.connect({ skipMigrations: true }); + server = await startOrdhookServer({ db }); + fastify = await buildApiServer({ db }); + }); + + test('shuts down when streaming on replay mode', async () => { + const payload1 = new TestChainhookPayloadBuilder() + .apply() + .block({ + height: 767430, + hash: '0x163de66dc9c0949905bfe8e148bde04600223cf88d19f26fdbeba1d6e6fa0f88', + timestamp: 1676913207, + }) + .transaction({ + hash: '0x0268dd9743c862d80ab02cb1d0228036cfe172522850eb96be60cfee14b31fb8', + }) + .inscriptionRevealed({ + content_bytes: '0x303030303030303030303030', + content_type: 'text/plain;charset=utf-8', + content_length: 12, + inscription_number: { classic: 0, jubilee: 0 }, + inscription_fee: 3425, + inscription_output_value: 10000, + inscription_id: '0268dd9743c862d80ab02cb1d0228036cfe172522850eb96be60cfee14b31fb8i0', + inscriber_address: 'bc1p3cyx5e2hgh53w7kpxcvm8s4kkega9gv5wfw7c4qxsvxl0u8x834qf0u2td', + ordinal_number: 125348773618236, + ordinal_block_height: 566462, + ordinal_offset: 0, + satpoint_post_inscription: + '0x0268dd9743c862d80ab02cb1d0228036cfe172522850eb96be60cfee14b31fb8:0:0', + inscription_input_index: 0, + transfers_pre_inscription: 0, + tx_index: 0, + curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, + }) + .build(); + + const mockExit = jest.spyOn(process, 'exit').mockImplementation(); + const response = await server['fastify'].inject({ + method: 'POST', + url: `/payload`, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, + payload: payload1, + }); + expect(response.statusCode).toBe(200); + expect(mockExit).toHaveBeenCalled(); + mockExit.mockRestore(); + }); + + afterEach(async () => { + await server.close(); + await fastify.close(); + await db.close(); + await runMigrations(MIGRATIONS_DIR, 'down'); + }); +}); diff --git a/tests/ordhook/server.test.ts b/tests/ordhook/server.test.ts index efcacb15..30bfdd2e 100644 --- a/tests/ordhook/server.test.ts +++ b/tests/ordhook/server.test.ts @@ -18,7 +18,7 @@ describe('EventServer', () => { beforeEach(async () => { await runMigrations(MIGRATIONS_DIR, 'up'); - ENV.CHAINHOOK_AUTO_PREDICATE_REGISTRATION = false; + ENV.ORDHOOK_AUTO_PREDICATE_REGISTRATION = false; db = await PgStore.connect({ skipMigrations: true }); server = await startOrdhookServer({ db }); fastify = await buildApiServer({ db }); @@ -51,6 +51,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }; // Apply @@ -69,7 +74,7 @@ describe('EventServer', () => { const response = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: payload1, }); expect(response.statusCode).toBe(200); @@ -129,7 +134,7 @@ describe('EventServer', () => { const response2 = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: payload2, }); expect(response2.statusCode).toBe(200); @@ -169,6 +174,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -204,7 +214,7 @@ describe('EventServer', () => { const response = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: payload1, }); expect(response.statusCode).toBe(200); @@ -264,7 +274,7 @@ describe('EventServer', () => { const response2 = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: payload2, }); expect(response2.statusCode).toBe(200); @@ -306,6 +316,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 995, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .transaction({ hash: '7edaa48337a94da327b6262830505f116775a32db5ad4ad46e87ecea33f21bac', @@ -363,6 +378,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -394,13 +414,18 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await expect(db.updateInscriptions(errorPayload)).rejects.toThrow(BadPayloadRequestError); const response = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: errorPayload, }); expect(response.statusCode).toBe(400); @@ -436,6 +461,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -467,6 +497,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .transaction({ hash: '6891d374a17ba85f6b5514f2f7edc301c1c860284dff5a5c6e88ab3a20fcd8a5', @@ -489,13 +524,18 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await expect(db.updateInscriptions(errorPayload)).rejects.toThrow(BadPayloadRequestError); const response = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: errorPayload, }); expect(response.statusCode).toBe(400); @@ -531,6 +571,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ); @@ -562,6 +607,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .transaction({ hash: '6891d374a17ba85f6b5514f2f7edc301c1c860284dff5a5c6e88ab3a20fcd8a5', @@ -584,6 +634,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await expect(db.updateInscriptions(unboundPayload)).resolves.not.toThrow( @@ -620,6 +675,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build(); await db.updateInscriptions(payload); @@ -627,7 +687,7 @@ describe('EventServer', () => { const response = await server['fastify'].inject({ method: 'POST', url: `/payload`, - headers: { authorization: `Bearer ${ENV.CHAINHOOK_NODE_AUTH_TOKEN}` }, + headers: { authorization: `Bearer ${ENV.ORDHOOK_NODE_AUTH_TOKEN}` }, payload: payload, }); expect(response.statusCode).toBe(200); @@ -666,6 +726,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: { tag: 'x' }, + parent: null, }) .inscriptionRevealed({ content_bytes: '0x48656C6C6F', @@ -686,6 +751,11 @@ describe('EventServer', () => { transfers_pre_inscription: 0, tx_index: 0, curse_type: null, + inscription_pointer: null, + delegate: null, + metaprotocol: null, + metadata: null, + parent: null, }) .build() ) diff --git a/tests/setup.ts b/tests/setup.ts index c2ec989f..95438bc2 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -1,6 +1,8 @@ // ts-unused-exports:disable-next-line export default (): void => { - process.env.CHAINHOOK_NODE_AUTH_TOKEN = 'test'; - process.env.CHAINHOOK_NODE_RPC_HOST = 'test.chainhooks.com'; + process.env.ORDHOOK_NODE_AUTH_TOKEN = 'test'; + process.env.ORDHOOK_NODE_RPC_HOST = 'test.chainhooks.com'; + process.env.ORDHOOK_NODE_RPC_PORT = '13370'; + process.env.ORDHOOK_INGESTION_MODE = 'default'; process.env.PGDATABASE = 'postgres'; }; diff --git a/util/debug-server.ts b/util/debug-server.ts new file mode 100644 index 00000000..3537408a --- /dev/null +++ b/util/debug-server.ts @@ -0,0 +1,48 @@ +/** + * Ordhook Debug Server + * --- + * + * This file provides a quick way to start an Ordhook event server that only saves received payloads + * to local text files instead of attempting to process them into the Ordinals API database. + * + * You can use this tool to debug an Ordhook payload that is not being processed correctly into the + * API. + */ +import { + ChainhookEventObserver, + ChainhookNodeOptions, + Payload, + ServerOptions, +} from '@hirosystems/chainhook-client'; +import { ENV } from '../src/env'; +import { ORDHOOK_BASE_PATH } from '../src/ordhook/server'; +import { logger } from '@hirosystems/api-toolkit'; +import * as fs from 'fs'; +import * as path from 'path'; + +const serverOpts: ServerOptions = { + hostname: ENV.API_HOST, + port: ENV.EVENT_PORT, + auth_token: ENV.ORDHOOK_NODE_AUTH_TOKEN, + external_base_url: `http://${ENV.EXTERNAL_HOSTNAME}`, + wait_for_chainhook_node: false, + validate_chainhook_payloads: false, + body_limit: ENV.EVENT_SERVER_BODY_LIMIT, + node_type: 'ordhook', +}; +const ordhookOpts: ChainhookNodeOptions = { + base_url: ORDHOOK_BASE_PATH, +}; +const dirPath = path.join(__dirname, '../../tmp/debug-server/'); +fs.mkdirSync(dirPath, { recursive: true }); +logger.info(`DebugServer saving outputs to ${dirPath}`); + +const server = new ChainhookEventObserver(serverOpts, ordhookOpts); +server + .start([], async (uuid: string, payload: Payload) => { + logger.info(`DebugServer received payload from predicate ${uuid}`); + const filePath = path.join(dirPath, `${new Date().getTime()}.txt`); + fs.writeFileSync(filePath, JSON.stringify(payload, null, 2)); + return Promise.resolve(); + }) + .catch(err => logger.error(err));