Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support tenure change transactions in rosetta #2128

Merged
merged 2 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/api/controllers/db-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand Down
3 changes: 2 additions & 1 deletion src/api/rosetta-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
Expand All @@ -77,6 +77,7 @@ export const RosettaOperationTypes = arrayOfAllOpTypes([
'stack_stx',
'delegate_stx',
'revoke_delegate_stx',
'tenure_change',
]) as RosettaOperationType[];

export const RosettaOperationStatuses = [
Expand Down
21 changes: 21 additions & 0 deletions src/rosetta/rosetta-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import * as btc from 'bitcoinjs-lib';
import {
getTxFromDataStore,
getTxStatus,
getTxTenureChangeCauseString,
getTxTypeString,
parseContractCallMetadata,
} from '../api/controllers/db-controller';
Expand Down Expand Up @@ -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)}`);
}
Expand Down Expand Up @@ -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');

Expand Down
127 changes: 127 additions & 0 deletions tests/rosetta/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
Loading