From 1edb25697df689dbf1da5d412f5d40e4aac024f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20C=C3=A1rdenas?= Date: Fri, 30 Sep 2022 11:37:12 -0500 Subject: [PATCH] fix: support multiple BNS name events in the same transaction (#1337) * fix: multiple name transfers in same block * fix: support multiple name events in same tx --- src/datastore/common.ts | 4 +- src/datastore/pg-store.ts | 9 +- src/datastore/pg-write-store.ts | 33 +- src/event-stream/bns/bns-helpers.ts | 23 +- src/migrations/1608030374842_names.ts | 6 +- src/tests-bns/event-server-tests.ts | 441 ++++++++++++++++++++++++++ 6 files changed, 486 insertions(+), 30 deletions(-) diff --git a/src/datastore/common.ts b/src/datastore/common.ts index 22a7bb35..b3a7f500 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -517,11 +517,12 @@ export interface DbBnsName { expire_block: number; grace_period?: number; renewal_deadline?: number; - resolver?: string | undefined; + resolver?: string; zonefile: string; zonefile_hash: string; tx_id: string; tx_index: number; + event_index?: number; status?: string; canonical: boolean; } @@ -1106,6 +1107,7 @@ export interface BnsNameInsertValues { namespace_id: string; tx_index: number; tx_id: PgBytea; + event_index: number | null; status: string | null; canonical: boolean; index_block_hash: PgBytea; diff --git a/src/datastore/pg-store.ts b/src/datastore/pg-store.ts index e76fbcc0..2c5bcb59 100644 --- a/src/datastore/pg-store.ts +++ b/src/datastore/pg-store.ts @@ -3127,7 +3127,7 @@ export class PgStore { WHERE namespace_id = ${namespace} AND registered_at <= ${maxBlockHeight} AND canonical = true AND microblock_canonical = true - ORDER BY name, registered_at DESC, microblock_sequence DESC, tx_index DESC + ORDER BY name, registered_at DESC, microblock_sequence DESC, tx_index DESC, event_index DESC LIMIT 100 OFFSET ${offset} `; @@ -3187,7 +3187,10 @@ export class PgStore { AND n.registered_at <= ${maxBlockHeight} AND n.canonical = true AND n.microblock_canonical = true - ORDER BY n.registered_at DESC, n.microblock_sequence DESC, n.tx_index DESC + ORDER BY n.registered_at DESC, + n.microblock_sequence DESC, + n.tx_index DESC, + n.event_index DESC LIMIT 1 `; if (nameZonefile.length === 0) { @@ -3455,7 +3458,7 @@ export class PgStore { FROM names WHERE canonical = true AND microblock_canonical = true AND registered_at <= ${maxBlockHeight} - ORDER BY name, registered_at DESC, microblock_sequence DESC, tx_index DESC + ORDER BY name, registered_at DESC, microblock_sequence DESC, tx_index DESC, event_index DESC LIMIT 100 OFFSET ${offset} `; diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index 72f8ea59..fa9c4b36 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -1404,12 +1404,25 @@ export class PgWriteStore extends PgStore { namespace_id, tx_id, tx_index, + event_index, status, canonical, } = bnsName; - // Try to figure out the name's expiration block based on its namespace's lifetime. However, if - // the name was only transferred, keep the expiration from the last register/renewal we had. + // Try to figure out the name's expiration block based on its namespace's lifetime. let expireBlock = expire_block; + const namespaceLifetime = await sql<{ lifetime: number }[]>` + SELECT lifetime + FROM namespaces + WHERE namespace_id = ${namespace_id} + AND canonical = true AND microblock_canonical = true + ORDER BY namespace_id, ready_block DESC, microblock_sequence DESC, tx_index DESC + LIMIT 1 + `; + if (namespaceLifetime.length > 0) { + expireBlock = registered_at + namespaceLifetime[0].lifetime; + } + // If the name was transferred, keep the expiration from the last register/renewal we had (if + // any). if (status === 'name-transfer') { const prevExpiration = await sql<{ expire_block: number }[]>` SELECT expire_block @@ -1422,18 +1435,6 @@ export class PgWriteStore extends PgStore { if (prevExpiration.length > 0) { expireBlock = prevExpiration[0].expire_block; } - } else { - const namespaceLifetime = await sql<{ lifetime: number }[]>` - SELECT lifetime - FROM namespaces - WHERE namespace_id = ${namespace_id} - AND canonical = true AND microblock_canonical = true - ORDER BY namespace_id, ready_block DESC, microblock_sequence DESC, tx_index DESC - LIMIT 1 - `; - if (namespaceLifetime.length > 0) { - expireBlock = registered_at + namespaceLifetime[0].lifetime; - } } // If we didn't receive a zonefile, keep the last valid one. let finalZonefile = zonefile; @@ -1476,6 +1477,7 @@ export class PgWriteStore extends PgStore { namespace_id: namespace_id, tx_index: tx_index, tx_id: tx_id, + event_index: event_index ?? null, status: status ?? null, canonical: canonical, index_block_hash: blockData.index_block_hash, @@ -1486,7 +1488,7 @@ export class PgWriteStore extends PgStore { }; await sql` INSERT INTO names ${sql(nameValues)} - ON CONFLICT ON CONSTRAINT unique_name_tx_id_index_block_hash_microblock_hash DO + ON CONFLICT ON CONSTRAINT unique_name_tx_id_index_block_hash_microblock_hash_event_index DO UPDATE SET address = EXCLUDED.address, registered_at = EXCLUDED.registered_at, @@ -1494,6 +1496,7 @@ export class PgWriteStore extends PgStore { zonefile_hash = EXCLUDED.zonefile_hash, namespace_id = EXCLUDED.namespace_id, tx_index = EXCLUDED.tx_index, + event_index = EXCLUDED.event_index, status = EXCLUDED.status, canonical = EXCLUDED.canonical, parent_index_block_hash = EXCLUDED.parent_index_block_hash, diff --git a/src/event-stream/bns/bns-helpers.ts b/src/event-stream/bns/bns-helpers.ts index 1166353b..7173ff80 100644 --- a/src/event-stream/bns/bns-helpers.ts +++ b/src/event-stream/bns/bns-helpers.ts @@ -1,5 +1,5 @@ import { BufferCV, ChainID, ClarityType, hexToCV, StringAsciiCV } from '@stacks/transactions'; -import { hexToBuffer, hexToUtf8String } from '../../helpers'; +import { bnsNameCV, hexToBuffer, hexToUtf8String } from '../../helpers'; import { CoreNodeEvent, CoreNodeEventType, @@ -281,6 +281,7 @@ export function parseNameRenewalWithNoZonefileHashFromContractCall( zonefile: '', tx_id: tx.parsed_tx.tx_id, tx_index: tx.core_tx.tx_index, + event_index: undefined, status: 'name-renewal', canonical: true, }; @@ -290,7 +291,7 @@ export function parseNameRenewalWithNoZonefileHashFromContractCall( export function parseNameFromContractEvent( event: SmartContractEvent, tx: CoreNodeParsedTxMessage, - txEvents: CoreNodeEvent[], + allEvents: CoreNodeEvent[], blockHeight: number, chainId: ChainID ): DbBnsName | undefined { @@ -303,24 +304,27 @@ export function parseNameFromContractEvent( } catch (error) { return; } - let name_address = attachment.attachment.metadata.tx_sender.address; + const fullName = `${attachment.attachment.metadata.name}.${attachment.attachment.metadata.namespace}`; + let ownerAddress = attachment.attachment.metadata.tx_sender.address; // Is this a `name-transfer`? If so, look for the new owner in an `nft_transfer` event bundled in // the same transaction. if (attachment.attachment.metadata.op === 'name-transfer') { - for (const txEvent of txEvents) { + for (const eventItem of allEvents) { if ( - txEvent.type === CoreNodeEventType.NftTransferEvent && - txEvent.nft_transfer_event.asset_identifier === `${getBnsContractID(chainId)}::names` + eventItem.txid === event.txid && + eventItem.type === CoreNodeEventType.NftTransferEvent && + eventItem.nft_transfer_event.asset_identifier === `${getBnsContractID(chainId)}::names` && + eventItem.nft_transfer_event.raw_value === bnsNameCV(fullName) ) { - name_address = txEvent.nft_transfer_event.recipient; + ownerAddress = eventItem.nft_transfer_event.recipient; break; } } } const name: DbBnsName = { - name: `${attachment.attachment.metadata.name}.${attachment.attachment.metadata.namespace}`, + name: fullName, namespace_id: attachment.attachment.metadata.namespace, - address: name_address, + address: ownerAddress, // expire_block will be calculated upon DB insert based on the namespace's lifetime. expire_block: 0, registered_at: blockHeight, @@ -329,6 +333,7 @@ export function parseNameFromContractEvent( zonefile: '', tx_id: event.txid, tx_index: tx.core_tx.tx_index, + event_index: event.event_index, status: attachment.attachment.metadata.op, canonical: true, }; diff --git a/src/migrations/1608030374842_names.ts b/src/migrations/1608030374842_names.ts index b745a769..374bca1c 100644 --- a/src/migrations/1608030374842_names.ts +++ b/src/migrations/1608030374842_names.ts @@ -52,6 +52,7 @@ export async function up(pgm: MigrationBuilder): Promise { type: 'smallint', notNull: true, }, + event_index: 'integer', status: { type: 'string', notNull: false @@ -89,10 +90,11 @@ export async function up(pgm: MigrationBuilder): Promise { { name: 'registered_at', sort: 'DESC' }, { name: 'microblock_sequence', sort: 'DESC' }, { name: 'tx_index', sort: 'DESC' }, + { name: 'event_index', sort: 'DESC' } ]); pgm.addConstraint( 'names', - 'unique_name_tx_id_index_block_hash_microblock_hash', - 'UNIQUE(name, tx_id, index_block_hash, microblock_hash)' + 'unique_name_tx_id_index_block_hash_microblock_hash_event_index', + 'UNIQUE(name, tx_id, index_block_hash, microblock_hash, event_index)' ); } diff --git a/src/tests-bns/event-server-tests.ts b/src/tests-bns/event-server-tests.ts index 7ff0401c..c3b3984e 100644 --- a/src/tests-bns/event-server-tests.ts +++ b/src/tests-bns/event-server-tests.ts @@ -586,6 +586,447 @@ describe('BNS event server tests', () => { expect(result[0].zonefile).toBe('$ORIGIN jnj.btc.\n$TTL 3600\n_http._tcp\tIN\tURI\t10\t1\t"https://gaia.blockstack.org/hub/1z8AzyhC42n8TvoFaUL2nscaCGHqQQWUr/profile.json"\n\n'); }); + test('name-register and name-transfer for several names in one block', async () => { + const block = new TestBlockBuilder({ + block_height: 1, + block_hash: '0x161bd86201417a55fb0dd851ac0e6b10c67a0b443e593008a4cf46fb6938b369', + index_block_hash: '0x8cc3d58350082f3161ae34deaad77c1c8887947ff0295be59ec5caccf984fe78', + burn_block_height: 756266, + burn_block_hash: '0x00000000000000000002e78c9c19a055ca0e680674e1a2f0f01a48c04a24f627', + burn_block_time: 1664489645, + }) + .addTx({ + tx_id: '0x1234', + sender_address: 'SP3GWTV1SMF9HDS4VY5NMM833CHH266W4YBASVYMZ' + }) + .addTxBnsNamespace({ + namespace_id: 'btc', + lifetime: 1000 + }) + .build(); + await db.update(block); + const microblock = new TestMicroblockStreamBuilder() + .addMicroblock({ + microblock_hash: '0xc44f4e3ed66bacaaa5cbe5b9c35b4e2ce2467933b57974fa03b539a2b2b88063', + microblock_sequence: 0, + parent_index_block_hash: '0x8cc3d58350082f3161ae34deaad77c1c8887947ff0295be59ec5caccf984fe78' + }) + .build(); + await db.updateMicroblocks(microblock); + + const payload = { + // In the block message, events are not sorted by `event_index`. + "events": [ + { + "txid": "0xd5803813a0befbf7e426ca897a5940c691a18e5959170e12ddb9e71c91ea4f12", + "type": "nft_mint_event", + "committed": true, + "event_index": 405, + "nft_mint_event": { + "raw_value": "0x0c00000002046e616d6502000000086b6574656c6f6e65096e616d6573706163650200000003627463", + "recipient": "SP253DQBW2ZBKE10PBQVBDJ5XSQQ4P06PVP9PR6S8", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0xd5803813a0befbf7e426ca897a5940c691a18e5959170e12ddb9e71c91ea4f12", + "type": "contract_event", + "committed": true, + "event_index": 406, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e64657801000000000000000000000000000144ea04686173680200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb086d657461646174610c00000004046e616d6502000000086b6574656c6f6e65096e616d6573706163650200000003627463026f700d0000000d6e616d652d72656769737465720974782d73656e64657205168a36dd7c17d73704165df6b6c8bdcdee4b00d6dd", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + }, + { + "txid": "0xa106e30d1df4607a993ff2ec0d68a4acfb3d5ab2ae597179869df8d6da8f1b95", + "type": "nft_transfer_event", + "committed": true, + "event_index": 407, + "nft_transfer_event": { + "sender": "SP253DQBW2ZBKE10PBQVBDJ5XSQQ4P06PVP9PR6S8", + "raw_value": "0x0c00000002046e616d6502000000086b6574656c6f6e65096e616d6573706163650200000003627463", + "recipient": "SP2WPXVTZE2RG4SZGJT5HTZ7JK6CAWTEV0A55HFH7", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0xa106e30d1df4607a993ff2ec0d68a4acfb3d5ab2ae597179869df8d6da8f1b95", + "type": "contract_event", + "committed": true, + "event_index": 408, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e64657801000000000000000000000000000144eb04686173680200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb086d657461646174610c00000004046e616d6502000000086b6574656c6f6e65096e616d6573706163650200000003627463026f700d0000000d6e616d652d7472616e736665720974782d73656e64657205168a36dd7c17d73704165df6b6c8bdcdee4b00d6dd", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + }, + { + "txid": "0x1784633b879ffcf15c18dcf627047a44358f2f0660c14f5188c9f17b28abb8af", + "type": "nft_mint_event", + "committed": true, + "event_index": 381, + "nft_mint_event": { + "raw_value": "0x0c00000002046e616d65020000000f637269636b6574776972656c657373096e616d6573706163650200000003627463", + "recipient": "SP2MM4ETXDE26HQ64F29VG05Q577DEPTSDJ2DQV8N", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0x1784633b879ffcf15c18dcf627047a44358f2f0660c14f5188c9f17b28abb8af", + "type": "contract_event", + "committed": true, + "event_index": 382, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e64657801000000000000000000000000000144e204686173680200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb086d657461646174610c00000004046e616d65020000000f637269636b6574776972656c657373096e616d6573706163650200000003627463026f700d0000000d6e616d652d72656769737465720974782d73656e6465720516a9423b5d6b8468dcc47893b800b729ced75b596c", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + }, + { + "txid": "0x28715dc6e09e75cec4d26d6a52426c8cc13c6e5a16d5252886c33ffc6bcceef7", + "type": "nft_transfer_event", + "committed": true, + "event_index": 389, + "nft_transfer_event": { + "sender": "SP2MM4ETXDE26HQ64F29VG05Q577DEPTSDJ2DQV8N", + "raw_value": "0x0c00000002046e616d65020000000f637269636b6574776972656c657373096e616d6573706163650200000003627463", + "recipient": "SP1QFKSVQP3J2PF45KFFCVBR4Q24Y09G0PJDECHS7", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0x28715dc6e09e75cec4d26d6a52426c8cc13c6e5a16d5252886c33ffc6bcceef7", + "type": "contract_event", + "committed": true, + "event_index": 390, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e64657801000000000000000000000000000144e304686173680200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb086d657461646174610c00000004046e616d65020000000f637269636b6574776972656c657373096e616d6573706163650200000003627463026f700d0000000d6e616d652d7472616e736665720974782d73656e6465720516a9423b5d6b8468dcc47893b800b729ced75b596c", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + } + ], + "block_hash": "0x41e158fe192103d2a5f895c6f9093a548ecc35db3a4c3c5de0e616fd3894338e", + "miner_txid": "0x9c48f6c748177cd049db40172e5044e5a98f8fe5b798f33212f876121e764b72", + "block_height": 2, + "transactions": [ + { + "txid": "0x1784633b879ffcf15c18dcf627047a44358f2f0660c14f5188c9f17b28abb8af", + "raw_tx": "0x00000000010400a9423b5d6b8468dcc47893b800b729ced75b596c00000000000000010000000000014ed6010055b3a6e2581eaaf686bc9596a4c9cf62cbdb30ffaad167c094824b5d89598ce1101ff56aeb58e2020c10954da05cd80b733ec79ecd71db1921aa202d377aac740302000000000216000000000000000000000000000000000000000003626e730d6e616d652d7265676973746572000000040200000003627463020000000f637269636b6574776972656c65737302000000149a3db4f009ad960c5a0cad7ad9c19f21fa0fe3680200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", + "status": "success", + "tx_index": 274, + "raw_result": "0x0703", + "contract_abi": null, + "execution_cost": { + "runtime": 311527, + "read_count": 17, + "read_length": 43206, + "write_count": 4, + "write_length": 242 + }, + "microblock_hash": null, + "microblock_sequence": null, + "microblock_parent_hash": null + }, + { + "txid": "0x28715dc6e09e75cec4d26d6a52426c8cc13c6e5a16d5252886c33ffc6bcceef7", + "raw_tx": "0x00000000010400a9423b5d6b8468dcc47893b800b729ced75b596c00000000000000020000000000014941010173c47aad0c8e5e8e2c655f488e4b8f514a63fd0190ae392f6cc6f22c1ec93aa44facb412a9d6504efd7945eeb52407011069ca1d3a7138e7a889c7c15aa82df2030200000001020216a9423b5d6b8468dcc47893b800b729ced75b596c16000000000000000000000000000000000000000003626e73056e616d65730c00000002046e616d65020000000f637269636b6574776972656c657373096e616d6573706163650200000003627463100216000000000000000000000000000000000000000003626e730d6e616d652d7472616e73666572000000040200000003627463020000000f637269636b6574776972656c65737305166ef9e777b0e42b3c859bdecdaf04b889e02600b40a0200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", + "status": "success", + "tx_index": 276, + "raw_result": "0x0703", + "contract_abi": null, + "execution_cost": { + "runtime": 183670, + "read_count": 19, + "read_length": 44047, + "write_count": 5, + "write_length": 266 + }, + "microblock_hash": null, + "microblock_sequence": null, + "microblock_parent_hash": null + }, + { + "txid": "0xd5803813a0befbf7e426ca897a5940c691a18e5959170e12ddb9e71c91ea4f12", + "raw_tx": "0x000000000104008a36dd7c17d73704165df6b6c8bdcdee4b00d6dd0000000000000001000000000001449f0101bd23afc22da4e356847d76d07261a861488389d4864c8d42ce002be439e0e78b3aa1088a8aaac189f7c85e674fd871b787f1fb0cd5a19acd827a011f5e38921c0302000000000216000000000000000000000000000000000000000003626e730d6e616d652d726567697374657200000004020000000362746302000000086b6574656c6f6e6502000000146cd23e487d9068d24e1e1bc90636a6e48c1546a50200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", + "status": "success", + "tx_index": 285, + "raw_result": "0x0703", + "contract_abi": null, + "execution_cost": { + "runtime": 229244, + "read_count": 17, + "read_length": 43199, + "write_count": 4, + "write_length": 228 + }, + "microblock_hash": null, + "microblock_sequence": null, + "microblock_parent_hash": null + }, + { + "txid": "0xa106e30d1df4607a993ff2ec0d68a4acfb3d5ab2ae597179869df8d6da8f1b95", + "raw_tx": "0x000000000104008a36dd7c17d73704165df6b6c8bdcdee4b00d6dd00000000000000020000000000015cb70101ac9a2e87c627c605ac68f0c40d59ff6bd5543705a5710ee4679d936a664d20f60a0b91e98770cb3597ea25af005e9eb083a827e860b6ba975c0a819205b4792f0302000000010202168a36dd7c17d73704165df6b6c8bdcdee4b00d6dd16000000000000000000000000000000000000000003626e73056e616d65730c00000002046e616d6502000000086b6574656c6f6e65096e616d6573706163650200000003627463100216000000000000000000000000000000000000000003626e730d6e616d652d7472616e7366657200000004020000000362746302000000086b6574656c6f6e650516b96eef5f70b10267f0968b1d7cf29998ae69db020a0200000014b472a266d0bd89c13706a4132ccfb16f7c3b9fcb", + "status": "success", + "tx_index": 286, + "raw_result": "0x0703", + "contract_abi": null, + "execution_cost": { + "runtime": 183264, + "read_count": 19, + "read_length": 44026, + "write_count": 5, + "write_length": 252 + }, + "microblock_hash": null, + "microblock_sequence": null, + "microblock_parent_hash": null + } + ], + "anchored_cost": { + "runtime": 37717625, + "read_count": 3184, + "read_length": 10513899, + "write_count": 710, + "write_length": 42932 + }, + "burn_block_hash": "0x0000000000000000000213c1512c2bffae7378f2b890bfea3ee6dc8e2e7836a2", + "burn_block_time": 1664490688, + "index_block_hash": "0x2eb444d32bb66a6acc3ba66aedabbb19c3adde8b6a9717765960bdc67ea32070", + "burn_block_height": 756268, + "parent_block_hash": "0x161bd86201417a55fb0dd851ac0e6b10c67a0b443e593008a4cf46fb6938b369", + "parent_microblock": "0xc44f4e3ed66bacaaa5cbe5b9c35b4e2ce2467933b57974fa03b539a2b2b88063", + "matured_miner_rewards": [], + "parent_burn_block_hash": "0x00000000000000000002e78c9c19a055ca0e680674e1a2f0f01a48c04a24f627", + "parent_index_block_hash": "0x8cc3d58350082f3161ae34deaad77c1c8887947ff0295be59ec5caccf984fe78", + "parent_burn_block_height": 756266, + "confirmed_microblocks_cost": { + "runtime": 5707388, + "read_count": 545, + "read_length": 2095326, + "write_count": 127, + "write_length": 8025 + }, + "parent_microblock_sequence": 0, + "parent_burn_block_timestamp": 1664489645 + }; + + await httpPostRequest({ + host: '127.0.0.1', + port: eventServer.serverAddress.port, + path: '/new_block', + headers: { 'Content-Type': 'application/json' }, + body: Buffer.from(JSON.stringify(payload), 'utf8'), + throwOnNotOK: true, + }); + + const name = await db.getName({ + name: 'cricketwireless.btc', + includeUnanchored: true, + chainId: ChainID.Mainnet + }); + expect(name.found).toBe(true); + expect(name.result?.namespace_id).toBe('btc'); + expect(name.result?.tx_id).toBe('0x28715dc6e09e75cec4d26d6a52426c8cc13c6e5a16d5252886c33ffc6bcceef7'); + expect(name.result?.status).toBe('name-transfer'); + expect(name.result?.address).toBe('SP1QFKSVQP3J2PF45KFFCVBR4Q24Y09G0PJDECHS7'); + }); + + test('name-register and name-transfer in same tx from non-BNS contract', async () => { + const block = new TestBlockBuilder({ + block_height: 1, + block_hash: '0x08cdd83644176e87cd5fdc584a5193de84c4c54cbe8b3839225e75f396f64468', + index_block_hash: '0x82239cdbd3903ca032d300101990120947132a8a005a92d7a1cdcd5a61b35ba1', + burn_block_height: 749980, + burn_block_hash: '0x000000000000000000089afaf672605818e368521d9ad2d8e4b5763956b75363', + burn_block_time: 1660833970, + }) + .addTx({ + tx_id: '0x1234', + sender_address: 'SP3GWTV1SMF9HDS4VY5NMM833CHH266W4YBASVYMZ' + }) + .addTxBnsNamespace({ + namespace_id: 'mega', + lifetime: 1000 + }) + .build(); + await db.update(block); + const microblock = new TestMicroblockStreamBuilder() + .addMicroblock({ + microblock_hash: '0x2ad76cc1eadb6e0dd155a7b5ac82ff81a2c664552dacb99a524a410856330529', + microblock_sequence: 0, + parent_index_block_hash: '0x82239cdbd3903ca032d300101990120947132a8a005a92d7a1cdcd5a61b35ba1' + }) + .build(); + await db.updateMicroblocks(microblock); + + const payload = { + "events": [ + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "contract_event", + "committed": true, + "event_index": 85, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e646578010000000000000000000000000000f65804686173680200000000086d657461646174610c00000004046e616d650200000003617065096e616d65737061636502000000046d656761026f700d0000000d6e616d652d7472616e736665720974782d73656e64657206161809f2ab9182b6ff1678f82846131c0709e51cf91b72796465722d68616e646c65732d636f6e74726f6c6c65722d7631", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "stx_burn_event", + "committed": true, + "event_index": 81, + "stx_burn_event": { + "amount": "1", + "sender": "SPC0KWNBJ61BDZRPF3W2GHGK3G3GKS8WZ7ND33PS.ryder-handles-controller-v1" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "nft_transfer_event", + "committed": true, + "event_index": 84, + "nft_transfer_event": { + "sender": "SPC0KWNBJ61BDZRPF3W2GHGK3G3GKS8WZ7ND33PS.ryder-handles-controller-v1", + "raw_value": "0x0c00000002046e616d650200000003617065096e616d65737061636502000000046d656761", + "recipient": "SPV48Q8E5WP4TCQ63E9TV6KF9R4HP01Z8WS3FBTG", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "nft_mint_event", + "committed": true, + "event_index": 82, + "nft_mint_event": { + "raw_value": "0x0c00000002046e616d650200000003617065096e616d65737061636502000000046d656761", + "recipient": "SPC0KWNBJ61BDZRPF3W2GHGK3G3GKS8WZ7ND33PS.ryder-handles-controller-v1", + "asset_identifier": "SP000000000000000000002Q6VF78.bns::names" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "stx_transfer_event", + "committed": true, + "event_index": 79, + "stx_transfer_event": { + "amount": "3000000", + "sender": "SPC0KWNBJ61BDZRPF3W2GHGK3G3GKS8WZ7ND33PS.ryder-handles-controller-v1", + "recipient": "SP2J9XB6CNJX9C36D5SY4J85SA0P1MQX7R5VFKZZX" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "stx_transfer_event", + "committed": true, + "event_index": 80, + "stx_transfer_event": { + "amount": "1", + "sender": "SP3C8QH2R3909YQZ7WVZ71N8RJ6Y0P317T8MG8XSZ", + "recipient": "SPC0KWNBJ61BDZRPF3W2GHGK3G3GKS8WZ7ND33PS.ryder-handles-controller-v1" + } + }, + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "type": "contract_event", + "committed": true, + "event_index": 83, + "contract_event": { + "topic": "print", + "raw_value": "0x0c000000010a6174746163686d656e740c00000003106174746163686d656e742d696e646578010000000000000000000000000000f65704686173680200000000086d657461646174610c00000004046e616d650200000003617065096e616d65737061636502000000046d656761026f700d0000000d6e616d652d72656769737465720974782d73656e64657206161809f2ab9182b6ff1678f82846131c0709e51cf91b72796465722d68616e646c65732d636f6e74726f6c6c65722d7631", + "contract_identifier": "SP000000000000000000002Q6VF78.bns" + } + } + ], + "block_hash": "0xbcf632eaa887b66a6356bf9410eb61377cced2d3f444a2286fb59b12a63e48e4", + "miner_txid": "0x037d5016d21839a46f136ad846ea99967eda65bf5cdb31feabc60c8eaef5b96d", + "block_height": 2, + "transactions": [ + { + "txid": "0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16", + "raw_tx": "0x00000000010400d88bc4581a409f5fe7e6fe70d51891bc0b0c27d2000000000000001100000000000003e80001fff157398074931aca859d34de1f3070359b8493033cd79fde329ecd66e4bd090235cf61f9916f76cdbaa37ff4d2ee3358322f6a58ec2fb05717115c913fd6e103010000000002161809f2ab9182b6ff1678f82846131c0709e51cf91b72796465722d68616e646c65732d636f6e74726f6c6c65722d76310d6e616d652d72656769737465720000000602000000046d656761020000000361706502000000057337306b35020000004107d00910104bba0ee68b131ceead109ccea598a267a2000140b3277809f1ab535dcef753028c00e7239be1477801ac7d5b8c10e0a7b242261285212da194bdad01051636445d0e2f2c4d32e61b93ad9a6f4e091b003f470200000000", + "status": "success", + "tx_index": 25, + "raw_result": "0x0703", + "contract_abi": null, + "execution_cost": { + "runtime": 643399, + "read_count": 69, + "read_length": 231108, + "write_count": 16, + "write_length": 1948 + }, + "microblock_hash": null, + "microblock_sequence": null, + "microblock_parent_hash": null + } + ], + "anchored_cost": { + "runtime": 39996577, + "read_count": 4234, + "read_length": 13859444, + "write_count": 676, + "write_length": 53049 + }, + "burn_block_hash": "0x0000000000000000000867b5dd6ec7ebb50404acabcdb35193b6b2fcd3ea7a37", + "burn_block_time": 1660834638, + "index_block_hash": "0xe43e505d4c7ca5f64a6d9617fbb658a84344610eb0e6495f8f9b7ab3b2648f61", + "burn_block_height": 749981, + "parent_block_hash": "0x08cdd83644176e87cd5fdc584a5193de84c4c54cbe8b3839225e75f396f64468", + "parent_microblock": "0x2ad76cc1eadb6e0dd155a7b5ac82ff81a2c664552dacb99a524a410856330529", + "matured_miner_rewards": [], + "parent_burn_block_hash": "0x000000000000000000089afaf672605818e368521d9ad2d8e4b5763956b75363", + "parent_index_block_hash": "0x82239cdbd3903ca032d300101990120947132a8a005a92d7a1cdcd5a61b35ba1", + "parent_burn_block_height": 749980, + "confirmed_microblocks_cost": { + "runtime": 0, + "read_count": 0, + "read_length": 0, + "write_count": 0, + "write_length": 0 + }, + "parent_microblock_sequence": 0, + "parent_burn_block_timestamp": 1660833970 + }; + + await httpPostRequest({ + host: '127.0.0.1', + port: eventServer.serverAddress.port, + path: '/new_block', + headers: { 'Content-Type': 'application/json' }, + body: Buffer.from(JSON.stringify(payload), 'utf8'), + throwOnNotOK: true, + }); + + const name = await db.getName({ + name: 'ape.mega', + includeUnanchored: true, + chainId: ChainID.Mainnet + }); + expect(name.found).toBe(true); + expect(name.result?.namespace_id).toBe('mega'); + expect(name.result?.tx_id).toBe('0xf9f9144793f6d4da9aba92a54ab601eb23bfe7f44c1edb29c2920bf5e7d2ac16'); + expect(name.result?.status).toBe('name-transfer'); + expect(name.result?.expire_block).toBe(1002); + expect(name.result?.address).toBe('SPV48Q8E5WP4TCQ63E9TV6KF9R4HP01Z8WS3FBTG'); + + const list = await db.getNamesList({ page: 0, includeUnanchored: true }); + expect(list.results.length).toBe(1); + expect(list.results).toStrictEqual(['ape.mega']); + + const namespaceList = await db.getNamespaceNamesList({ + namespace: 'mega', + page: 0, + includeUnanchored: true + }); + expect(namespaceList.results.length).toBe(1); + expect(namespaceList.results).toStrictEqual(['ape.mega']); + }); + afterEach(async () => { await eventServer.closeAsync(); await db?.close();