From e0deec10f9a1941dd1abc66705297f9d9a137ed6 Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Mon, 22 Sep 2025 13:52:26 -0600 Subject: [PATCH 1/3] fix: include block times in redis updates --- src/datastore/common.ts | 8 +++++++- src/datastore/pg-write-store.ts | 11 ++++++++++- src/datastore/redis-notifier.ts | 20 ++++++++++++-------- tests/api/datastore.test.ts | 10 +++++----- 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/datastore/common.ts b/src/datastore/common.ts index 9ddba7656..52b726542 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -1110,8 +1110,14 @@ export interface DbPoxCycleSignerStacker { stacker_type: 'solo' | 'pooled'; } +export interface BlockHeader { + index_block_hash: string; + block_height: number; + block_time: number; +} + interface ReOrgEntities { - blockHeaders: { index_block_hash: string; block_height: number }[]; + blockHeaders: BlockHeader[]; blocks: number; microblockHashes: string[]; microblocks: number; diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index f95ee8be0..cf5e4bd85 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -406,7 +406,14 @@ export class PgWriteStore extends PgStore { } }); if (isCanonical) { - await this.redisNotifier?.notify(reorg, data.block.index_block_hash, data.block.block_height); + await this.redisNotifier?.notify( + { + index_block_hash: data.block.index_block_hash, + block_height: data.block.block_height, + block_time: data.block.block_time, + }, + reorg + ); } // Do we have an IBD height defined in ENV? If so, check if this block update reached it. const ibdHeight = getIbdBlockHeight(); @@ -3590,6 +3597,7 @@ export class PgWriteStore extends PgStore { updatedEntities.markedCanonical.blockHeaders.unshift({ index_block_hash: restoredBlockResult[0].index_block_hash, block_height: restoredBlockResult[0].block_height, + block_time: restoredBlockResult[0].block_time, }); // Orphan the now conflicting block at the same height @@ -3632,6 +3640,7 @@ export class PgWriteStore extends PgStore { updatedEntities.markedNonCanonical.blockHeaders.unshift({ index_block_hash: orphanedBlockResult[0].index_block_hash, block_height: orphanedBlockResult[0].block_height, + block_time: orphanedBlockResult[0].block_time, }); const markNonCanonicalResult = await this.markEntitiesCanonical( sql, diff --git a/src/datastore/redis-notifier.ts b/src/datastore/redis-notifier.ts index a34f06789..f88d9ba5f 100644 --- a/src/datastore/redis-notifier.ts +++ b/src/datastore/redis-notifier.ts @@ -1,5 +1,5 @@ import Redis, { Cluster, RedisOptions } from 'ioredis'; -import { ReOrgUpdatedEntities } from './common'; +import { BlockHeader, ReOrgUpdatedEntities } from './common'; import { ChainID } from '@stacks/transactions'; import { getApiConfiguredChainID } from '../helpers'; import { logger } from '@hirosystems/api-toolkit'; @@ -24,32 +24,36 @@ export class RedisNotifier { /** * Broadcast index progress message to the Redis queue. * @param reOrg - The re-org updated entities, if any - * @param indexBlockHash - Block hash of the newest canonical block - * @param blockHeight - Block height of the newest canonical block + * @param block - The newest canonical block */ - async notify(reOrg: ReOrgUpdatedEntities, indexBlockHash: string, blockHeight: number) { + async notify(block: BlockHeader, reOrg: ReOrgUpdatedEntities) { + const time = Date.now(); const message = { - id: `stacks-${blockHeight}-${indexBlockHash}-${Date.now()}`, + id: `stacks-${block.block_height}-${block.index_block_hash}-${time}`, payload: { chain: 'stacks', network: this.chainId === ChainID.Mainnet ? 'mainnet' : 'testnet', + time, apply_blocks: [ ...reOrg.markedCanonical.blockHeaders.map(block => ({ hash: block.index_block_hash, index: block.block_height, + time: block.block_time, })), { - hash: indexBlockHash, - index: blockHeight, + hash: block.index_block_hash, + index: block.block_height, + time: block.block_time, }, ], rollback_blocks: reOrg.markedNonCanonical.blockHeaders.map(block => ({ hash: block.index_block_hash, index: block.block_height, + time: block.block_time, })), }, }; - logger.debug(message, 'RedisNotifier broadcasting index progress message'); + logger.info(message, 'RedisNotifier broadcasting index progress message'); await this.redis.rpush(this.queue, JSON.stringify(message)); } diff --git a/tests/api/datastore.test.ts b/tests/api/datastore.test.ts index 909a8d495..5c9561f02 100644 --- a/tests/api/datastore.test.ts +++ b/tests/api/datastore.test.ts @@ -3773,10 +3773,10 @@ describe('postgres datastore', () => { const expectedReorgResult: ReOrgUpdatedEntities = { markedCanonical: { blockHeaders: [ - { block_height: 1, index_block_hash: '0xaa' }, - { block_height: 2, index_block_hash: '0xbb' }, - { block_height: 3, index_block_hash: '0xcc' }, - { block_height: 4, index_block_hash: '0xdd' }, + { block_height: 1, index_block_hash: '0xaa', block_time: 1234 }, + { block_height: 2, index_block_hash: '0xbb', block_time: 1234 }, + { block_height: 3, index_block_hash: '0xcc', block_time: 1234 }, + { block_height: 4, index_block_hash: '0xdd', block_time: 1234 }, ], blocks: 4, microblocks: 0, @@ -3799,7 +3799,7 @@ describe('postgres datastore', () => { poxSigners: 0, }, markedNonCanonical: { - blockHeaders: [{ block_height: 3, index_block_hash: '0xccbb' }], + blockHeaders: [{ block_height: 3, index_block_hash: '0xccbb', block_time: 1234 }], blocks: 1, microblocks: 0, microblockHashes: [], From e7e6302bb974acc9d9d87a88215bd8805df5ee2f Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Mon, 22 Sep 2025 15:38:39 -0600 Subject: [PATCH 2/3] fix: tests --- tests/api/redis-notifier.test.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/api/redis-notifier.test.ts b/tests/api/redis-notifier.test.ts index 0236574ba..7430ef785 100644 --- a/tests/api/redis-notifier.test.ts +++ b/tests/api/redis-notifier.test.ts @@ -44,14 +44,15 @@ describe('redis notifier', () => { block_height: 1, block_hash: '0x1234', index_block_hash: '0x1234', + block_time: 1234, }).build(); await db.update(block1); expect(messages.length).toBe(1); - expect(JSON.parse(messages[0]).payload).toEqual({ + expect(JSON.parse(messages[0]).payload).toContain({ chain: 'stacks', network: 'mainnet', - apply_blocks: [{ hash: '0x1234', index: 1 }], + apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], rollback_blocks: [], }); }); @@ -62,13 +63,14 @@ describe('redis notifier', () => { block_height: 1, block_hash: '0x1234', index_block_hash: '0x1234', + block_time: 1234, }).build() ); expect(messages.length).toBe(1); - expect(JSON.parse(messages[0]).payload).toEqual({ + expect(JSON.parse(messages[0]).payload).toContain({ chain: 'stacks', network: 'mainnet', - apply_blocks: [{ hash: '0x1234', index: 1 }], + apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], rollback_blocks: [], }); @@ -78,13 +80,14 @@ describe('redis notifier', () => { block_hash: '0x1235', index_block_hash: '0x1235', parent_index_block_hash: '0x1234', + block_time: 1234, }).build() ); expect(messages.length).toBe(2); - expect(JSON.parse(messages[1]).payload).toEqual({ + expect(JSON.parse(messages[1]).payload).toContain({ chain: 'stacks', network: 'mainnet', - apply_blocks: [{ hash: '0x1235', index: 2 }], + apply_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], rollback_blocks: [], }); @@ -95,6 +98,7 @@ describe('redis notifier', () => { block_hash: '0x1235aa', index_block_hash: '0x1235aa', parent_index_block_hash: '0x1234', + block_time: 1234, }).build() ); expect(messages.length).toBe(2); @@ -106,17 +110,18 @@ describe('redis notifier', () => { block_hash: '0x1236', index_block_hash: '0x1236', parent_index_block_hash: '0x1235aa', + block_time: 1234, }).build() ); expect(messages.length).toBe(3); - expect(JSON.parse(messages[2]).payload).toEqual({ + expect(JSON.parse(messages[2]).payload).toContain({ chain: 'stacks', network: 'mainnet', apply_blocks: [ - { hash: '0x1235aa', index: 2 }, - { hash: '0x1236', index: 3 }, + { hash: '0x1235aa', index: 2, time: 1234 }, + { hash: '0x1236', index: 3, time: 1234 }, ], - rollback_blocks: [{ hash: '0x1235', index: 2 }], + rollback_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], }); }); }); From 6068abbf53801775a76d00655d907940230a80ee Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Mon, 22 Sep 2025 16:01:32 -0600 Subject: [PATCH 3/3] fix: tests again --- tests/api/redis-notifier.test.ts | 62 ++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/tests/api/redis-notifier.test.ts b/tests/api/redis-notifier.test.ts index 7430ef785..79660805f 100644 --- a/tests/api/redis-notifier.test.ts +++ b/tests/api/redis-notifier.test.ts @@ -49,12 +49,14 @@ describe('redis notifier', () => { await db.update(block1); expect(messages.length).toBe(1); - expect(JSON.parse(messages[0]).payload).toContain({ - chain: 'stacks', - network: 'mainnet', - apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], - rollback_blocks: [], - }); + expect(JSON.parse(messages[0]).payload).toEqual( + expect.objectContaining({ + chain: 'stacks', + network: 'mainnet', + apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], + rollback_blocks: [], + }) + ); }); test('updates redis with re-orgs', async () => { @@ -67,12 +69,14 @@ describe('redis notifier', () => { }).build() ); expect(messages.length).toBe(1); - expect(JSON.parse(messages[0]).payload).toContain({ - chain: 'stacks', - network: 'mainnet', - apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], - rollback_blocks: [], - }); + expect(JSON.parse(messages[0]).payload).toEqual( + expect.objectContaining({ + chain: 'stacks', + network: 'mainnet', + apply_blocks: [{ hash: '0x1234', index: 1, time: 1234 }], + rollback_blocks: [], + }) + ); await db.update( new TestBlockBuilder({ @@ -84,12 +88,14 @@ describe('redis notifier', () => { }).build() ); expect(messages.length).toBe(2); - expect(JSON.parse(messages[1]).payload).toContain({ - chain: 'stacks', - network: 'mainnet', - apply_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], - rollback_blocks: [], - }); + expect(JSON.parse(messages[1]).payload).toEqual( + expect.objectContaining({ + chain: 'stacks', + network: 'mainnet', + apply_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], + rollback_blocks: [], + }) + ); // Re-org block 2, should not send a message because this block is not canonical await db.update( @@ -114,14 +120,16 @@ describe('redis notifier', () => { }).build() ); expect(messages.length).toBe(3); - expect(JSON.parse(messages[2]).payload).toContain({ - chain: 'stacks', - network: 'mainnet', - apply_blocks: [ - { hash: '0x1235aa', index: 2, time: 1234 }, - { hash: '0x1236', index: 3, time: 1234 }, - ], - rollback_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], - }); + expect(JSON.parse(messages[2]).payload).toEqual( + expect.objectContaining({ + chain: 'stacks', + network: 'mainnet', + apply_blocks: [ + { hash: '0x1235aa', index: 2, time: 1234 }, + { hash: '0x1236', index: 3, time: 1234 }, + ], + rollback_blocks: [{ hash: '0x1235', index: 2, time: 1234 }], + }) + ); }); });