Skip to content

Commit bfbf65c

Browse files
rafaelcrzone117x
andauthored
fix(rosetta): support tenure change transactions (#2128)
* fix: support tenure change transactions in rosetta * test: add test for rosetta tenure-change tx --------- Co-authored-by: Matthew Little <[email protected]>
1 parent f17106a commit bfbf65c

File tree

4 files changed

+152
-2
lines changed

4 files changed

+152
-2
lines changed

src/api/controllers/db-controller.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export function getTxTypeString(typeId: DbTxTypeId): Transaction['tx_type'] {
124124
return 'poison_microblock';
125125
case DbTxTypeId.Coinbase:
126126
case DbTxTypeId.CoinbaseToAltRecipient:
127+
case DbTxTypeId.NakamotoCoinbase:
127128
return 'coinbase';
128129
case DbTxTypeId.TenureChange:
129130
return 'tenure_change';
@@ -145,7 +146,7 @@ function getTxAnchorModeString(anchorMode: number): TransactionAnchorModeType {
145146
}
146147
}
147148

148-
function getTxTenureChangeCauseString(cause: number) {
149+
export function getTxTenureChangeCauseString(cause: number) {
149150
switch (cause) {
150151
case 0:
151152
return 'block_found';

src/api/rosetta-constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export enum RosettaOperationType {
5252
StackStx = 'stack_stx',
5353
DelegateStx = 'delegate_stx',
5454
RevokeDelegateStx = 'revoke_delegate_stx',
55-
// todo: add new pox-2 methods
55+
TenureChange = 'tenure_change',
5656
}
5757

5858
type RosettaOperationTypeUnion = `${RosettaOperationType}`;
@@ -77,6 +77,7 @@ export const RosettaOperationTypes = arrayOfAllOpTypes([
7777
'stack_stx',
7878
'delegate_stx',
7979
'revoke_delegate_stx',
80+
'tenure_change',
8081
]) as RosettaOperationType[];
8182

8283
export const RosettaOperationStatuses = [

src/rosetta/rosetta-helpers.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import * as btc from 'bitcoinjs-lib';
2525
import {
2626
getTxFromDataStore,
2727
getTxStatus,
28+
getTxTenureChangeCauseString,
2829
getTxTypeString,
2930
parseContractCallMetadata,
3031
} from '../api/controllers/db-controller';
@@ -170,6 +171,9 @@ async function getOperationsInternal(
170171
case RosettaOperationType.PoisonMicroblock:
171172
operations.push(makePoisonMicroblockOperation(tx, 0));
172173
break;
174+
case RosettaOperationType.TenureChange:
175+
operations.push(makeTenureChangeOperation(tx, operations.length));
176+
break;
173177
default:
174178
throw new Error(`Unexpected tx type: ${JSON.stringify(txType)}`);
175179
}
@@ -727,6 +731,23 @@ function makePoisonMicroblockOperation(tx: BaseTx, index: number): RosettaOperat
727731
return sender;
728732
}
729733

734+
function makeTenureChangeOperation(tx: BaseTx, index: number): RosettaOperation {
735+
return {
736+
operation_identifier: { index: index },
737+
type: RosettaOperationType.TenureChange,
738+
status: getTxStatus(tx.status),
739+
metadata: {
740+
tenure_consensus_hash: tx.tenure_change_tenure_consensus_hash as string,
741+
prev_tenure_consensus_hash: tx.tenure_change_prev_tenure_consensus_hash as string,
742+
burn_view_consensus_hash: tx.tenure_change_burn_view_consensus_hash as string,
743+
previous_tenure_end: tx.tenure_change_previous_tenure_end as string,
744+
previous_tenure_blocks: tx.tenure_change_previous_tenure_blocks as number,
745+
cause: getTxTenureChangeCauseString(tx.tenure_change_cause as number),
746+
pubkey_hash: tx.tenure_change_pubkey_hash as string,
747+
},
748+
};
749+
}
750+
730751
export function publicKeyToBitcoinAddress(publicKey: string, network: string): string | undefined {
731752
const publicKeyBuffer = Buffer.from(publicKey, 'hex');
732753

tests/rosetta/api.test.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,133 @@ describe('Rosetta API', () => {
730730
});
731731
});
732732

733+
test('epoch3 tenure-change block/transaction', async () => {
734+
const parentData = new TestBlockBuilder().addTx().build();
735+
const block1: TestBlockArgs = {
736+
block_height: 2,
737+
block_hash: '0xd0dd05e3d0a1bd60640c9d9d30d57012ffe47b52fe643140c39199c757d37e3f',
738+
index_block_hash: '0x6a36c14514047074c2877065809bbb70d81d52507747f4616da997deb7228fad',
739+
parent_index_block_hash: parentData.block.index_block_hash,
740+
parent_block_hash: parentData.block.block_hash,
741+
parent_microblock_hash: '0x0000000000000000000000000000000000000000000000000000000000000000',
742+
burn_block_hash: '0xfe15c0d3ebe314fad720a08b839a004c2e6386f5aecc19ec74807d1920cb6aeb',
743+
miner_txid: '0x0000000000000000000000000000000000000000000000000000000000000000',
744+
};
745+
const txTenureChange1: TestTxArgs = {
746+
tx_id: '0xc152de9376bab4fc27291c9cd088643698290a12bb511d768f873cb3d280eb48',
747+
tx_index: 1,
748+
type_id: DbTxTypeId.TenureChange,
749+
status: DbTxStatus.Success,
750+
raw_result: '0x0703',
751+
canonical: true,
752+
microblock_canonical: true,
753+
microblock_sequence: 2147483647,
754+
microblock_hash: '0x00',
755+
fee_rate: 0n,
756+
sender_address: 'ST1HB1T8WRNBYB0Y3T7WXZS38NKKPTBR3EG9EPJKR',
757+
tenure_change_tenure_consensus_hash: '0x2fedd90a5f318ed8cec419fd1c6656b5af452497',
758+
tenure_change_prev_tenure_consensus_hash: '0x5104aae6d442b49c8e8d2031df7f40b67528e654',
759+
tenure_change_burn_view_consensus_hash: '0x2fedd90a5f318ed8cec419fd1c6656b5af452497',
760+
tenure_change_previous_tenure_end:
761+
'0xb77b061202b1e6dce889ba1633efa969d3c24679d32a7542d29015ee94e8a860',
762+
tenure_change_previous_tenure_blocks: 9,
763+
tenure_change_cause: 0,
764+
tenure_change_pubkey_hash: '0x62b4273562dfa3825496094507564bf2b30c8b11',
765+
};
766+
const blockData1 = new TestBlockBuilder(block1).addTx(txTenureChange1).build();
767+
768+
await db.update(parentData);
769+
await db.update(blockData1);
770+
771+
const query1 = await supertest(api.server)
772+
.post(`/rosetta/v1/block/transaction`)
773+
.send({
774+
network_identifier: { blockchain: 'stacks', network: 'testnet' },
775+
block_identifier: {
776+
index: blockData1.block.block_height,
777+
hash: blockData1.block.block_hash,
778+
},
779+
transaction_identifier: { hash: txTenureChange1.tx_id },
780+
});
781+
expect(query1.status).toBe(200);
782+
expect(query1.type).toBe('application/json');
783+
expect(query1.body).toEqual({
784+
transaction_identifier: {
785+
hash: txTenureChange1.tx_id,
786+
},
787+
operations: [
788+
{
789+
operation_identifier: {
790+
index: 0,
791+
},
792+
type: 'tenure_change',
793+
status: 'success',
794+
metadata: {
795+
tenure_consensus_hash: txTenureChange1.tenure_change_tenure_consensus_hash,
796+
prev_tenure_consensus_hash: txTenureChange1.tenure_change_prev_tenure_consensus_hash,
797+
burn_view_consensus_hash: txTenureChange1.tenure_change_burn_view_consensus_hash,
798+
previous_tenure_end: txTenureChange1.tenure_change_previous_tenure_end,
799+
previous_tenure_blocks: txTenureChange1.tenure_change_previous_tenure_blocks,
800+
cause: 'block_found',
801+
pubkey_hash: txTenureChange1.tenure_change_pubkey_hash,
802+
},
803+
},
804+
],
805+
});
806+
807+
const query2 = await supertest(api.address)
808+
.post(`/rosetta/v1/block`)
809+
.send({
810+
network_identifier: { blockchain: 'stacks', network: 'testnet' },
811+
block_identifier: { index: blockData1.block.block_height },
812+
});
813+
expect(query1.status).toBe(200);
814+
expect(query1.type).toBe('application/json');
815+
const expected: RosettaBlockResponse = {
816+
block: {
817+
block_identifier: {
818+
index: blockData1.block.block_height,
819+
hash: blockData1.block.block_hash,
820+
},
821+
parent_block_identifier: {
822+
index: blockData1.block.block_height - 1,
823+
hash: blockData1.block.parent_block_hash,
824+
},
825+
timestamp: blockData1.block.burn_block_time * 1000,
826+
transactions: [
827+
{
828+
transaction_identifier: {
829+
hash: txTenureChange1.tx_id as string,
830+
},
831+
operations: [
832+
{
833+
operation_identifier: {
834+
index: 0,
835+
},
836+
type: 'tenure_change',
837+
status: 'success',
838+
metadata: {
839+
tenure_consensus_hash: txTenureChange1.tenure_change_tenure_consensus_hash,
840+
prev_tenure_consensus_hash:
841+
txTenureChange1.tenure_change_prev_tenure_consensus_hash,
842+
burn_view_consensus_hash: txTenureChange1.tenure_change_burn_view_consensus_hash,
843+
previous_tenure_end: txTenureChange1.tenure_change_previous_tenure_end,
844+
previous_tenure_blocks: txTenureChange1.tenure_change_previous_tenure_blocks,
845+
cause: 'block_found',
846+
pubkey_hash: txTenureChange1.tenure_change_pubkey_hash,
847+
},
848+
},
849+
],
850+
},
851+
],
852+
metadata: {
853+
burn_block_height: blockData1.block.burn_block_height,
854+
},
855+
},
856+
};
857+
expect(query2.body).toEqual(expected);
858+
});
859+
733860
test('block/transaction - invalid transaction hash', async () => {
734861
const query1 = await supertest(api.server)
735862
.post(`/rosetta/v1/block/transaction`)

0 commit comments

Comments
 (0)