From 686e545637ccfb5322fce0d4f658187482d2154b Mon Sep 17 00:00:00 2001 From: Rafael Cardenas Date: Sun, 20 Oct 2024 14:38:56 -0600 Subject: [PATCH 1/2] fix: support tenure change transactions in rosetta --- src/api/controllers/db-controller.ts | 3 ++- src/api/rosetta-constants.ts | 3 ++- src/rosetta/rosetta-helpers.ts | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/api/controllers/db-controller.ts b/src/api/controllers/db-controller.ts index 91724252c..63e725353 100644 --- a/src/api/controllers/db-controller.ts +++ b/src/api/controllers/db-controller.ts @@ -124,6 +124,7 @@ export function getTxTypeString(typeId: DbTxTypeId): Transaction['tx_type'] { return 'poison_microblock'; case DbTxTypeId.Coinbase: case DbTxTypeId.CoinbaseToAltRecipient: + case DbTxTypeId.NakamotoCoinbase: return 'coinbase'; case DbTxTypeId.TenureChange: return 'tenure_change'; @@ -145,7 +146,7 @@ function getTxAnchorModeString(anchorMode: number): TransactionAnchorModeType { } } -function getTxTenureChangeCauseString(cause: number) { +export function getTxTenureChangeCauseString(cause: number) { switch (cause) { case 0: return 'block_found'; diff --git a/src/api/rosetta-constants.ts b/src/api/rosetta-constants.ts index 3a391272c..d3ff59130 100644 --- a/src/api/rosetta-constants.ts +++ b/src/api/rosetta-constants.ts @@ -52,7 +52,7 @@ export enum RosettaOperationType { StackStx = 'stack_stx', DelegateStx = 'delegate_stx', RevokeDelegateStx = 'revoke_delegate_stx', - // todo: add new pox-2 methods + TenureChange = 'tenure_change', } type RosettaOperationTypeUnion = `${RosettaOperationType}`; @@ -77,6 +77,7 @@ export const RosettaOperationTypes = arrayOfAllOpTypes([ 'stack_stx', 'delegate_stx', 'revoke_delegate_stx', + 'tenure_change', ]) as RosettaOperationType[]; export const RosettaOperationStatuses = [ diff --git a/src/rosetta/rosetta-helpers.ts b/src/rosetta/rosetta-helpers.ts index 2f924e82f..5e8c4e294 100644 --- a/src/rosetta/rosetta-helpers.ts +++ b/src/rosetta/rosetta-helpers.ts @@ -25,6 +25,7 @@ import * as btc from 'bitcoinjs-lib'; import { getTxFromDataStore, getTxStatus, + getTxTenureChangeCauseString, getTxTypeString, parseContractCallMetadata, } from '../api/controllers/db-controller'; @@ -170,6 +171,9 @@ async function getOperationsInternal( case RosettaOperationType.PoisonMicroblock: operations.push(makePoisonMicroblockOperation(tx, 0)); break; + case RosettaOperationType.TenureChange: + operations.push(makeTenureChangeOperation(tx, operations.length)); + break; default: throw new Error(`Unexpected tx type: ${JSON.stringify(txType)}`); } @@ -727,6 +731,23 @@ function makePoisonMicroblockOperation(tx: BaseTx, index: number): RosettaOperat return sender; } +function makeTenureChangeOperation(tx: BaseTx, index: number): RosettaOperation { + return { + operation_identifier: { index: index }, + type: RosettaOperationType.TenureChange, + status: getTxStatus(tx.status), + metadata: { + tenure_consensus_hash: tx.tenure_change_tenure_consensus_hash as string, + prev_tenure_consensus_hash: tx.tenure_change_prev_tenure_consensus_hash as string, + burn_view_consensus_hash: tx.tenure_change_burn_view_consensus_hash as string, + previous_tenure_end: tx.tenure_change_previous_tenure_end as string, + previous_tenure_blocks: tx.tenure_change_previous_tenure_blocks as number, + cause: getTxTenureChangeCauseString(tx.tenure_change_cause as number), + pubkey_hash: tx.tenure_change_pubkey_hash as string, + }, + }; +} + export function publicKeyToBitcoinAddress(publicKey: string, network: string): string | undefined { const publicKeyBuffer = Buffer.from(publicKey, 'hex'); From 8629e148474d4b9e4f5bd8653efb7ec524430d08 Mon Sep 17 00:00:00 2001 From: Matthew Little Date: Mon, 21 Oct 2024 12:34:58 +0200 Subject: [PATCH 2/2] test: add test for rosetta tenure-change tx --- tests/rosetta/api.test.ts | 127 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/tests/rosetta/api.test.ts b/tests/rosetta/api.test.ts index 2bce79f64..40cd9d204 100644 --- a/tests/rosetta/api.test.ts +++ b/tests/rosetta/api.test.ts @@ -730,6 +730,133 @@ describe('Rosetta API', () => { }); }); + test('epoch3 tenure-change block/transaction', async () => { + const parentData = new TestBlockBuilder().addTx().build(); + const block1: TestBlockArgs = { + block_height: 2, + block_hash: '0xd0dd05e3d0a1bd60640c9d9d30d57012ffe47b52fe643140c39199c757d37e3f', + index_block_hash: '0x6a36c14514047074c2877065809bbb70d81d52507747f4616da997deb7228fad', + parent_index_block_hash: parentData.block.index_block_hash, + parent_block_hash: parentData.block.block_hash, + parent_microblock_hash: '0x0000000000000000000000000000000000000000000000000000000000000000', + burn_block_hash: '0xfe15c0d3ebe314fad720a08b839a004c2e6386f5aecc19ec74807d1920cb6aeb', + miner_txid: '0x0000000000000000000000000000000000000000000000000000000000000000', + }; + const txTenureChange1: TestTxArgs = { + tx_id: '0xc152de9376bab4fc27291c9cd088643698290a12bb511d768f873cb3d280eb48', + tx_index: 1, + type_id: DbTxTypeId.TenureChange, + status: DbTxStatus.Success, + raw_result: '0x0703', + canonical: true, + microblock_canonical: true, + microblock_sequence: 2147483647, + microblock_hash: '0x00', + fee_rate: 0n, + sender_address: 'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR', + tenure_change_tenure_consensus_hash: '0x2fedd90a5f318ed8cec419fd1c6656b5af452497', + tenure_change_prev_tenure_consensus_hash: '0x5104aae6d442b49c8e8d2031df7f40b67528e654', + tenure_change_burn_view_consensus_hash: '0x2fedd90a5f318ed8cec419fd1c6656b5af452497', + tenure_change_previous_tenure_end: + '0xb77b061202b1e6dce889ba1633efa969d3c24679d32a7542d29015ee94e8a860', + tenure_change_previous_tenure_blocks: 9, + tenure_change_cause: 0, + tenure_change_pubkey_hash: '0x62b4273562dfa3825496094507564bf2b30c8b11', + }; + const blockData1 = new TestBlockBuilder(block1).addTx(txTenureChange1).build(); + + await db.update(parentData); + await db.update(blockData1); + + const query1 = await supertest(api.server) + .post(`/rosetta/v1/block/transaction`) + .send({ + network_identifier: { blockchain: 'stacks', network: 'testnet' }, + block_identifier: { + index: blockData1.block.block_height, + hash: blockData1.block.block_hash, + }, + transaction_identifier: { hash: txTenureChange1.tx_id }, + }); + expect(query1.status).toBe(200); + expect(query1.type).toBe('application/json'); + expect(query1.body).toEqual({ + transaction_identifier: { + hash: txTenureChange1.tx_id, + }, + operations: [ + { + operation_identifier: { + index: 0, + }, + type: 'tenure_change', + status: 'success', + metadata: { + tenure_consensus_hash: txTenureChange1.tenure_change_tenure_consensus_hash, + prev_tenure_consensus_hash: txTenureChange1.tenure_change_prev_tenure_consensus_hash, + burn_view_consensus_hash: txTenureChange1.tenure_change_burn_view_consensus_hash, + previous_tenure_end: txTenureChange1.tenure_change_previous_tenure_end, + previous_tenure_blocks: txTenureChange1.tenure_change_previous_tenure_blocks, + cause: 'block_found', + pubkey_hash: txTenureChange1.tenure_change_pubkey_hash, + }, + }, + ], + }); + + const query2 = await supertest(api.address) + .post(`/rosetta/v1/block`) + .send({ + network_identifier: { blockchain: 'stacks', network: 'testnet' }, + block_identifier: { index: blockData1.block.block_height }, + }); + expect(query1.status).toBe(200); + expect(query1.type).toBe('application/json'); + const expected: RosettaBlockResponse = { + block: { + block_identifier: { + index: blockData1.block.block_height, + hash: blockData1.block.block_hash, + }, + parent_block_identifier: { + index: blockData1.block.block_height - 1, + hash: blockData1.block.parent_block_hash, + }, + timestamp: blockData1.block.burn_block_time * 1000, + transactions: [ + { + transaction_identifier: { + hash: txTenureChange1.tx_id as string, + }, + operations: [ + { + operation_identifier: { + index: 0, + }, + type: 'tenure_change', + status: 'success', + metadata: { + tenure_consensus_hash: txTenureChange1.tenure_change_tenure_consensus_hash, + prev_tenure_consensus_hash: + txTenureChange1.tenure_change_prev_tenure_consensus_hash, + burn_view_consensus_hash: txTenureChange1.tenure_change_burn_view_consensus_hash, + previous_tenure_end: txTenureChange1.tenure_change_previous_tenure_end, + previous_tenure_blocks: txTenureChange1.tenure_change_previous_tenure_blocks, + cause: 'block_found', + pubkey_hash: txTenureChange1.tenure_change_pubkey_hash, + }, + }, + ], + }, + ], + metadata: { + burn_block_height: blockData1.block.burn_block_height, + }, + }, + }; + expect(query2.body).toEqual(expected); + }); + test('block/transaction - invalid transaction hash', async () => { const query1 = await supertest(api.server) .post(`/rosetta/v1/block/transaction`)