From bd13962dacc4023a247da40e06c6861cd1e8f2bf Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Mon, 21 Oct 2024 15:45:01 +0200 Subject: [PATCH] fix(rosetta): use Nakamoto block timestamps for epoch3/Nakamoto block responses (#2132) --- src/api/controllers/db-controller.ts | 9 ++- tests/rosetta/api.test.ts | 88 ++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/api/controllers/db-controller.ts b/src/api/controllers/db-controller.ts index 63e725353..2c466967a 100644 --- a/src/api/controllers/db-controller.ts +++ b/src/api/controllers/db-controller.ts @@ -541,10 +541,17 @@ export async function getRosettaBlockFromDataStore( } } + // In epoch2.x, only the burn_block_time is consensus-level. Starting in epoch3, Stacks blocks include a consensus-level timestamp. + // Use `signer_bitvec` field to determine if the block is from epoch3. + let timestamp = dbBlock.burn_block_time * 1000; + if (dbBlock.signer_bitvec) { + timestamp = dbBlock.block_time * 1000; + } + const apiBlock: RosettaBlock = { block_identifier: { index: dbBlock.block_height, hash: dbBlock.block_hash }, parent_block_identifier, - timestamp: dbBlock.burn_block_time * 1000, + timestamp: timestamp, transactions: blockTxs.found ? blockTxs.result : [], metadata: { burn_block_height: dbBlock.burn_block_height, diff --git a/tests/rosetta/api.test.ts b/tests/rosetta/api.test.ts index 40cd9d204..8ba073993 100644 --- a/tests/rosetta/api.test.ts +++ b/tests/rosetta/api.test.ts @@ -460,6 +460,94 @@ describe('Rosetta API', () => { }); }); + test('block - Nakamoto timestamps', async () => { + const parentData = new TestBlockBuilder({ + block_height: 0, + }).build(); + + // Epoch2.x block + const block1 = new TestBlockBuilder({ + block_height: 1, + block_hash: '0x1234', + index_block_hash: '0x123456', + parent_block_hash: parentData.block.block_hash, + parent_index_block_hash: parentData.block.index_block_hash, + }).build(); + block1.block.burn_block_time = 1222; + block1.block.block_time = 1333; + block1.block.signer_bitvec = null; + + // Epoch3 block + const block2 = new TestBlockBuilder({ + block_height: 2, + block_hash: '0x2234', + index_block_hash: '0x223456', + parent_block_hash: block1.block.block_hash, + parent_index_block_hash: block1.block.index_block_hash, + }).build(); + block2.block.burn_block_time = 2222; + block2.block.block_time = 2333; + block2.block.signer_bitvec = '1111'; + + await db.update(parentData); + await db.update(block1); + await db.update(block2); + + const query1 = await supertest(api.address) + .post(`/rosetta/v1/block`) + .send({ + network_identifier: { blockchain: 'stacks', network: 'testnet' }, + block_identifier: { index: block1.block.block_height }, + }); + expect(query1.status).toBe(200); + expect(query1.type).toBe('application/json'); + const expected1: RosettaBlockResponse = { + block: { + block_identifier: { + index: block1.block.block_height, + hash: block1.block.block_hash, + }, + parent_block_identifier: { + index: 1, + hash: '0x1234', + }, + timestamp: block1.block.burn_block_time * 1000, // epoch2.x, should be burn-block-time + transactions: [], + metadata: { + burn_block_height: block1.block.burn_block_height, + }, + }, + }; + expect(query1.body).toEqual(expected1); + + const query2 = await supertest(api.address) + .post(`/rosetta/v1/block`) + .send({ + network_identifier: { blockchain: 'stacks', network: 'testnet' }, + block_identifier: { index: block2.block.block_height }, + }); + expect(query2.status).toBe(200); + expect(query2.type).toBe('application/json'); + const expected2: RosettaBlockResponse = { + block: { + block_identifier: { + index: block2.block.block_height, + hash: block2.block.block_hash, + }, + parent_block_identifier: { + index: block2.block.block_height - 1, + hash: block2.block.parent_block_hash, + }, + timestamp: block2.block.block_time * 1000, // epoch3, should be Stacks-block-time + transactions: [], + metadata: { + burn_block_height: block2.block.burn_block_height, + }, + }, + }; + expect(query2.body).toEqual(expected2); + }); + test('stx-transfer-memo block/transaction', async () => { const parentData = new TestBlockBuilder().addTx().build(); const block: TestBlockArgs = {