From 03f467cb362a5b48b699d0a7ac97c2d457047f24 Mon Sep 17 00:00:00 2001 From: usagi32 Date: Thu, 24 Oct 2024 04:23:30 +0530 Subject: [PATCH] feat: replacing txid when mempool transaction is dropped by fee and replaced with another --- .../1715844926068_add-replacing-txid.js | 13 ++++ src/api/controllers/db-controller.ts | 1 + src/api/schemas/entities/transactions.ts | 3 + src/datastore/common.ts | 4 ++ src/datastore/helpers.ts | 12 ++++ src/datastore/pg-write-store.ts | 8 ++- src/event-stream/core-node-message.ts | 1 + src/event-stream/event-server.ts | 4 +- tests/api/address.test.ts | 2 + tests/api/cache-control.test.ts | 2 +- tests/api/datastore.test.ts | 1 + tests/api/mempool.test.ts | 65 +++++++++++++++++++ tests/api/microblock.test.ts | 2 + tests/api/search.test.ts | 6 ++ tests/api/tx.test.ts | 6 ++ tests/utils/test-builders.ts | 2 + 16 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 migrations/1715844926068_add-replacing-txid.js diff --git a/migrations/1715844926068_add-replacing-txid.js b/migrations/1715844926068_add-replacing-txid.js new file mode 100644 index 000000000..eae07ba20 --- /dev/null +++ b/migrations/1715844926068_add-replacing-txid.js @@ -0,0 +1,13 @@ +/* eslint-disable camelcase */ +/** @param { import("node-pg-migrate").MigrationBuilder } pgm */ + +exports.up = pgm => { + pgm.addColumn('mempool_txs', { + replacing_txid: { + type: 'string', + notNull: false, + } + }); +}; + + diff --git a/src/api/controllers/db-controller.ts b/src/api/controllers/db-controller.ts index ec70ce459..9d5d707fa 100644 --- a/src/api/controllers/db-controller.ts +++ b/src/api/controllers/db-controller.ts @@ -1146,6 +1146,7 @@ function parseDbAbstractMempoolTx( const abstractMempoolTx: AbstractMempoolTransaction = { ...baseTx, tx_status: getTxStatusString(dbMempoolTx.status) as MempoolTransactionStatus, + replacing_txid: dbMempoolTx.replacing_txid, receipt_time: dbMempoolTx.receipt_time, receipt_time_iso: unixEpochToIso(dbMempoolTx.receipt_time), }; diff --git a/src/api/schemas/entities/transactions.ts b/src/api/schemas/entities/transactions.ts index a683571e1..9077ea9de 100644 --- a/src/api/schemas/entities/transactions.ts +++ b/src/api/schemas/entities/transactions.ts @@ -428,6 +428,9 @@ export const AbstractMempoolTransactionProperties = { description: 'Status of the transaction', } ), + replacing_txid: Type.Optional(Type.String({ + description: 'The transaction ID used which is used to replace the previous', + })), receipt_time: Type.Integer({ description: 'A unix timestamp (in seconds) indicating when the transaction broadcast was received by the node.', diff --git a/src/datastore/common.ts b/src/datastore/common.ts index 60ae03642..818e06efd 100644 --- a/src/datastore/common.ts +++ b/src/datastore/common.ts @@ -303,6 +303,8 @@ export interface DbMempoolFeePriority { export interface DbMempoolTx extends BaseTx { pruned: boolean; + replacing_txid?: string + receipt_time: number; post_conditions: string; @@ -893,6 +895,7 @@ export interface MempoolTxQueryResult { type_id: number; anchor_mode: number; status: number; + replacing_txid?: string; receipt_time: number; receipt_block_height: number; @@ -1235,6 +1238,7 @@ export interface MempoolTxInsertValues { type_id: DbTxTypeId; anchor_mode: DbTxAnchorMode; status: DbTxStatus; + replacing_txid: string | null, receipt_time: number; receipt_block_height: number; post_conditions: PgBytea; diff --git a/src/datastore/helpers.ts b/src/datastore/helpers.ts index bdaf3729f..4c84b0b4c 100644 --- a/src/datastore/helpers.ts +++ b/src/datastore/helpers.ts @@ -132,6 +132,7 @@ export const MEMPOOL_TX_COLUMNS = [ 'type_id', 'anchor_mode', 'status', + 'replacing_txid', 'receipt_time', 'receipt_block_height', 'post_conditions', @@ -300,6 +301,7 @@ export function parseMempoolTxQueryResult(result: MempoolTxQueryResult): DbMempo type_id: result.type_id as DbTxTypeId, anchor_mode: result.anchor_mode as DbTxAnchorMode, status: result.status, + replacing_txid: result.replacing_txid, receipt_time: result.receipt_time, post_conditions: result.post_conditions, fee_rate: BigInt(result.fee_rate), @@ -1045,6 +1047,16 @@ export function getTxDbStatus( } } +export function getReplacingTx( + new_txid : string | null +): string { + if (new_txid === null){ + return ''; + } else{ + return new_txid; + } +} + /** * Extract tx-type specific data from a Transaction and into a tx db model. * @param txData - Transaction data to extract from. diff --git a/src/datastore/pg-write-store.ts b/src/datastore/pg-write-store.ts index 99cf8f0aa..e6e6af4e3 100644 --- a/src/datastore/pg-write-store.ts +++ b/src/datastore/pg-write-store.ts @@ -1904,6 +1904,7 @@ export class PgWriteStore extends PgStore { type_id: tx.type_id, anchor_mode: tx.anchor_mode, status: tx.status, + replacing_txid: tx.replacing_txid ?? null, receipt_time: tx.receipt_time, receipt_block_height: chainTip.block_height, post_conditions: tx.post_conditions, @@ -1943,6 +1944,7 @@ export class PgWriteStore extends PgStore { UPDATE mempool_txs SET pruned = false, status = ${DbTxStatus.Pending}, + replacing_txid = NULL, receipt_block_height = ${values[0].receipt_block_height}, receipt_time = ${values[0].receipt_time} WHERE tx_id IN ${sql(values.map(v => v.tx_id))} @@ -2069,12 +2071,12 @@ export class PgWriteStore extends PgStore { } } - async dropMempoolTxs({ status, txIds }: { status: DbTxStatus; txIds: string[] }): Promise { + async dropMempoolTxs({ status, txIds, replacing_txid }: { status: DbTxStatus; txIds: string[]; replacing_txid: string | null }): Promise { for (const batch of batchIterate(txIds, INSERT_BATCH_SIZE)) { const updateResults = await this.sql<{ tx_id: string }[]>` WITH pruned AS ( UPDATE mempool_txs - SET pruned = TRUE, status = ${status} + SET pruned = TRUE, status = ${status}, replacing_txid = ${replacing_txid} WHERE tx_id IN ${this.sql(batch)} AND pruned = FALSE RETURNING tx_id ), @@ -2683,7 +2685,7 @@ export class PgWriteStore extends PgStore { ), restored AS ( UPDATE mempool_txs - SET pruned = false, status = ${DbTxStatus.Pending} + SET pruned = false, status = ${DbTxStatus.Pending}, replacing_txid = NULL WHERE pruned = true AND tx_id IN (SELECT DISTINCT tx_id FROM affected_mempool_tx_ids) RETURNING tx_id ), diff --git a/src/event-stream/core-node-message.ts b/src/event-stream/core-node-message.ts index 9c8b276de..558a22184 100644 --- a/src/event-stream/core-node-message.ts +++ b/src/event-stream/core-node-message.ts @@ -380,6 +380,7 @@ export type CoreNodeDropMempoolTxReasonType = export interface CoreNodeDropMempoolTxMessage { dropped_txids: string[]; reason: CoreNodeDropMempoolTxReasonType; + new_txid: string | null; } export interface CoreNodeAttachmentMessage { diff --git a/src/event-stream/event-server.ts b/src/event-stream/event-server.ts index 2a2154b73..dc02cf00f 100644 --- a/src/event-stream/event-server.ts +++ b/src/event-stream/event-server.ts @@ -72,6 +72,7 @@ import { PgWriteStore } from '../datastore/pg-write-store'; import { createDbMempoolTxFromCoreMsg, createDbTxFromCoreMsg, + getReplacingTx, getTxDbStatus, } from '../datastore/helpers'; import { handleBnsImport } from '../import-v1'; @@ -169,7 +170,8 @@ async function handleDroppedMempoolTxsMessage( ): Promise { logger.debug(`Received ${msg.dropped_txids.length} dropped mempool txs`); const dbTxStatus = getTxDbStatus(msg.reason); - await db.dropMempoolTxs({ status: dbTxStatus, txIds: msg.dropped_txids }); + const replacing_txid = msg.new_txid; + await db.dropMempoolTxs({ status: dbTxStatus, txIds: msg.dropped_txids, replacing_txid }); } async function handleMicroblockMessage( diff --git a/tests/api/address.test.ts b/tests/api/address.test.ts index 6f68f4a95..f766575f9 100644 --- a/tests/api/address.test.ts +++ b/tests/api/address.test.ts @@ -2534,6 +2534,7 @@ describe('address tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: true, @@ -2588,6 +2589,7 @@ describe('address tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: true, diff --git a/tests/api/cache-control.test.ts b/tests/api/cache-control.test.ts index c7c010ad1..6d2dfc456 100644 --- a/tests/api/cache-control.test.ts +++ b/tests/api/cache-control.test.ts @@ -377,7 +377,7 @@ describe('cache-control tests', () => { expect(request3.text).toBe(''); // Drop one tx. - await db.dropMempoolTxs({ status: DbTxStatus.DroppedReplaceByFee, txIds: ['0x1101'] }); + await db.dropMempoolTxs({ status: DbTxStatus.DroppedReplaceByFee, txIds: ['0x1101'], replacing_txid: '0x1109' }); // Cache is now a miss. const request4 = await supertest(api.server) diff --git a/tests/api/datastore.test.ts b/tests/api/datastore.test.ts index e2f4174ae..16390a545 100644 --- a/tests/api/datastore.test.ts +++ b/tests/api/datastore.test.ts @@ -3323,6 +3323,7 @@ describe('postgres datastore', () => { token_transfer_memo: bufferToHex(Buffer.from('hi')), token_transfer_recipient_address: 'stx-recipient-addr', status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x', fee_rate: 1234n, sponsored: false, diff --git a/tests/api/mempool.test.ts b/tests/api/mempool.test.ts index 410d099da..35b501e13 100644 --- a/tests/api/mempool.test.ts +++ b/tests/api/mempool.test.ts @@ -190,6 +190,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-tx')), type_id: DbTxTypeId.Coinbase, status: DbTxStatus.Pending, + replacing_txid: '', receipt_time: 1594307695, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), post_conditions: '0x01f5', @@ -207,6 +208,7 @@ describe('mempool tests', () => { const expectedResp1 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -234,6 +236,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-tx')), type_id: DbTxTypeId.VersionedSmartContract, status: DbTxStatus.Pending, + replacing_txid: '', receipt_time: 1594307695, smart_contract_clarity_version: 2, smart_contract_contract_id: 'some-versioned-smart-contract', @@ -254,6 +257,7 @@ describe('mempool tests', () => { const expectedResp1 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'pending', + replacing_txid: '', tx_type: 'smart_contract', fee_rate: '1234', nonce: 0, @@ -285,6 +289,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-tx')), type_id: DbTxTypeId.Coinbase, status: DbTxStatus.Pending, + replacing_txid: '', receipt_time: 1594307695, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), post_conditions: '0x01f5', @@ -302,6 +307,7 @@ describe('mempool tests', () => { const expectedResp1 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -330,6 +336,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-tx')), type_id: DbTxTypeId.Coinbase, status: DbTxStatus.Pending, + replacing_txid: '', receipt_time: 1594307695, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), post_conditions: '0x01f5', @@ -365,12 +372,17 @@ describe('mempool tests', () => { receipt_time: 1594307706, }; + const new_txid1 : string = '0x8912000000000000000000000000000000000000000000000000000000000099'; + + const new_txid2 : string = '0x8912000000000000000000000000000000000000000000000000000000000100'; + await db.updateMempoolTxs({ mempoolTxs: [mempoolTx1, mempoolTx2, mempoolTx3, mempoolTx4, mempoolTx5, mempoolTx6], }); await db.dropMempoolTxs({ status: DbTxStatus.DroppedReplaceAcrossFork, txIds: [mempoolTx1.tx_id, mempoolTx2.tx_id], + replacing_txid: new_txid1, }); const searchResult1 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx1.tx_id}`); @@ -379,6 +391,7 @@ describe('mempool tests', () => { const expectedResp1 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'dropped_replace_across_fork', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000099', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -400,6 +413,7 @@ describe('mempool tests', () => { const expectedResp2 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001', tx_status: 'dropped_replace_across_fork', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000099', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -419,6 +433,7 @@ describe('mempool tests', () => { await db.dropMempoolTxs({ status: DbTxStatus.DroppedReplaceByFee, txIds: [mempoolTx3.tx_id], + replacing_txid: new_txid2, }); const searchResult3 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx3.tx_id}`); expect(searchResult3.status).toBe(200); @@ -426,6 +441,7 @@ describe('mempool tests', () => { const expectedResp3 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003', tx_status: 'dropped_replace_by_fee', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000100', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -444,6 +460,7 @@ describe('mempool tests', () => { await db.dropMempoolTxs({ status: DbTxStatus.DroppedTooExpensive, txIds: [mempoolTx4.tx_id], + replacing_txid: '', }); const searchResult4 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx4.tx_id}`); expect(searchResult4.status).toBe(200); @@ -451,6 +468,7 @@ describe('mempool tests', () => { const expectedResp4 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004', tx_status: 'dropped_too_expensive', + replacing_txid: '', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -469,6 +487,7 @@ describe('mempool tests', () => { await db.dropMempoolTxs({ status: DbTxStatus.DroppedStaleGarbageCollect, txIds: [mempoolTx5.tx_id], + replacing_txid: '', }); const searchResult5 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx5.tx_id}`); expect(searchResult5.status).toBe(200); @@ -476,6 +495,7 @@ describe('mempool tests', () => { const expectedResp5 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'dropped_stale_garbage_collect', + replacing_txid: '', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -494,6 +514,7 @@ describe('mempool tests', () => { await db.dropMempoolTxs({ status: DbTxStatus.DroppedProblematic, txIds: [mempoolTx6.tx_id], + replacing_txid: '', }); const searchResult6 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx6.tx_id}`); expect(searchResult6.status).toBe(200); @@ -501,6 +522,7 @@ describe('mempool tests', () => { const expectedResp6 = { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'dropped_problematic', + replacing_txid: '', tx_type: 'coinbase', fee_rate: '1234', nonce: 0, @@ -527,26 +549,32 @@ describe('mempool tests', () => { expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'dropped_problematic', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'dropped_stale_garbage_collect', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004', tx_status: 'dropped_too_expensive', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003', tx_status: 'dropped_replace_by_fee', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000100', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001', tx_status: 'dropped_replace_across_fork', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000099', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'dropped_replace_across_fork', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000099', }), ]), }) @@ -630,22 +658,27 @@ describe('mempool tests', () => { expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'dropped_problematic', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'dropped_stale_garbage_collect', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000004', tx_status: 'dropped_too_expensive', + replacing_txid: '', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000003', tx_status: 'dropped_replace_by_fee', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000100', }), expect.objectContaining({ tx_id: '0x8912000000000000000000000000000000000000000000000000000000000001', tx_status: 'dropped_replace_across_fork', + replacing_txid: '0x8912000000000000000000000000000000000000000000000000000000000099', }), ]), }) @@ -666,6 +699,7 @@ describe('mempool tests', () => { receipt_time: (new Date(`2020-07-09T15:14:0${i}Z`).getTime() / 1000) | 0, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -688,6 +722,7 @@ describe('mempool tests', () => { { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000007', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', receipt_time: 1594307647, receipt_time_iso: '2020-07-09T15:14:07.000Z', @@ -703,6 +738,7 @@ describe('mempool tests', () => { { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', receipt_time: 1594307646, receipt_time_iso: '2020-07-09T15:14:06.000Z', @@ -718,6 +754,7 @@ describe('mempool tests', () => { { tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', receipt_time: 1594307645, receipt_time_iso: '2020-07-09T15:14:05.000Z', @@ -804,6 +841,7 @@ describe('mempool tests', () => { type_id: xfer.type_id, receipt_time: (new Date(`2020-07-09T15:14:${paddedIndex}Z`).getTime() / 1000) | 0, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -848,6 +886,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, { @@ -867,6 +906,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, ], @@ -900,6 +940,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000007', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, { @@ -919,6 +960,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, ], @@ -952,6 +994,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, ], @@ -985,6 +1028,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000006', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, { @@ -1004,6 +1048,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000005', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, ], @@ -1037,6 +1082,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000010', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, { @@ -1051,6 +1097,7 @@ describe('mempool tests', () => { sponsored: false, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000008', tx_status: 'pending', + replacing_txid: '', tx_type: 'contract_call', contract_call: { contract_id: 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0', @@ -1089,6 +1136,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000010', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, { @@ -1103,6 +1151,7 @@ describe('mempool tests', () => { sponsored: false, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000008', tx_status: 'pending', + replacing_txid: '', tx_type: 'contract_call', contract_call: { contract_id: 'SP32AEEF6WW5Y0NMJ1S8SBSZDAY8R5J32NBZFPKKZ.free-punks-v0', @@ -1136,6 +1185,7 @@ describe('mempool tests', () => { sponsored: false, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000009', tx_status: 'pending', + replacing_txid: '', tx_type: 'smart_contract', smart_contract: { clarity_version: null, @@ -1174,6 +1224,7 @@ describe('mempool tests', () => { }, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000010', tx_status: 'pending', + replacing_txid: '', tx_type: 'token_transfer', }, ], @@ -1199,6 +1250,7 @@ describe('mempool tests', () => { type_id: DbTxTypeId.TokenTransfer, receipt_time: (new Date(`2020-07-09T15:14:${paddedIndex}Z`).getTime() / 1000) | 0, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 100n * BigInt(index + 1), sponsored: false, @@ -1320,10 +1372,13 @@ describe('mempool tests', () => { expectedContractDetails ); + const new_txid: string = '0x1232000000000000000000000000000000000000000000000000000000000001'; + // Dropped mempool tx await db.dropMempoolTxs({ status: DbTxStatus.DroppedReplaceAcrossFork, txIds: [mempoolTx1.tx_id], + replacing_txid: new_txid, }); const mempoolDropResults = await supertest(api.server).get(`/extended/v1/tx/mempool/dropped`); expect(mempoolDropResults.status).toBe(200); @@ -1367,6 +1422,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1394,6 +1450,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1444,6 +1501,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1466,6 +1524,7 @@ describe('mempool tests', () => { { tx_id: '0x521234', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', receipt_time: 1616063078, receipt_time_iso: '2021-03-18T10:24:38.000Z', @@ -1695,6 +1754,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1870,6 +1930,7 @@ describe('mempool tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1917,6 +1978,7 @@ describe('mempool tests', () => { await db.dropMempoolTxs({ status: DbTxStatus.DroppedStaleGarbageCollect, txIds: [mempoolTx.tx_id], + replacing_txid: '', }); // Verify tx is pruned from mempool @@ -2036,6 +2098,7 @@ describe('mempool tests', () => { anchor_mode: 3, raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), status: 1, + replacing_txid: '', post_conditions: '0x01f5', sponsored: false, sponsor_address: undefined, @@ -2056,6 +2119,7 @@ describe('mempool tests', () => { anchor_mode: 3, raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), status: 1, + replacing_txid: '', post_conditions: '0x01f5', sponsored: false, sponsor_address: undefined, @@ -2075,6 +2139,7 @@ describe('mempool tests', () => { anchor_mode: 3, raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), status: 1, + replacing_txid: '', post_conditions: '0x01f5', sponsored: false, sponsor_address: undefined, diff --git a/tests/api/microblock.test.ts b/tests/api/microblock.test.ts index a53064c67..5507f7f8f 100644 --- a/tests/api/microblock.test.ts +++ b/tests/api/microblock.test.ts @@ -505,11 +505,13 @@ describe('microblock tests', () => { const mempoolTx1: DbMempoolTxRaw = { ...mbTx1, pruned: false, + replacing_txid: '', receipt_time: 123456789, }; const mempoolTx2: DbMempoolTxRaw = { ...mbTx2, pruned: false, + replacing_txid: '', receipt_time: 123456789, }; await db.updateMempoolTxs({ mempoolTxs: [mempoolTx1, mempoolTx2] }); diff --git a/tests/api/search.test.ts b/tests/api/search.test.ts index 6cb5097f0..2d37a9007 100644 --- a/tests/api/search.test.ts +++ b/tests/api/search.test.ts @@ -116,6 +116,7 @@ describe('search tests', () => { receipt_time: 123456, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -329,6 +330,7 @@ describe('search tests', () => { receipt_time: 123456, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -483,6 +485,7 @@ describe('search tests', () => { sponsored: false, tx_id: '0x8912000000000000000000000000000000000000000000000000000000000000', tx_status: 'pending', + replacing_txid: '', tx_type: 'coinbase', }, }, @@ -969,6 +972,7 @@ describe('search tests', () => { smart_contract_contract_id: contractAddr2, smart_contract_source_code: '(some-src)', status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1592,6 +1596,7 @@ describe('search tests', () => { smart_contract_contract_id: contractAddr2, smart_contract_source_code: '(some-src)', status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -1633,6 +1638,7 @@ describe('search tests', () => { sponsored: false, tx_id: '0x1111882200000000000000000000000000000000000000000000000000000000', tx_status: 'pending', + replacing_txid: '', tx_type: 'smart_contract', }, }, diff --git a/tests/api/tx.test.ts b/tests/api/tx.test.ts index b4d333909..95e14997e 100644 --- a/tests/api/tx.test.ts +++ b/tests/api/tx.test.ts @@ -78,6 +78,7 @@ describe('tx tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-tx')), type_id: DbTxTypeId.Coinbase, status: DbTxStatus.Pending, + replacing_txid: '', receipt_time: 1594307695, coinbase_payload: bufferToHex(Buffer.from('coinbase hi')), post_conditions: '0x01f5', @@ -2741,6 +2742,7 @@ describe('tx tests', () => { raw_tx: bufferToHex(Buffer.from('test-raw-mempool-tx')), type_id: DbTxTypeId.Coinbase, status: 1, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 1234n, sponsored: false, @@ -3914,6 +3916,7 @@ describe('tx tests', () => { nonce: 0, raw_tx: bufferToHex(Buffer.from('')), status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 139200n, sponsored: false, @@ -3961,6 +3964,7 @@ describe('tx tests', () => { sponsored: false, tx_id: mempoolTx1.tx_id, tx_status: 'pending', + replacing_txid: '', tx_type: 'contract_call', }; const mempoolTxResult1 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx1.tx_id}`); @@ -3974,6 +3978,7 @@ describe('tx tests', () => { nonce: 0, raw_tx: bufferToHex(Buffer.from('')), status: DbTxStatus.Pending, + replacing_txid: '', post_conditions: '0x01f5', fee_rate: 139200n, sponsored: false, @@ -4022,6 +4027,7 @@ describe('tx tests', () => { sponsored: false, tx_id: mempoolTx2.tx_id, tx_status: 'pending', + replacing_txid: '', tx_type: 'contract_call', }; const mempoolTxResult2 = await supertest(api.server).get(`/extended/v1/tx/${mempoolTx2.tx_id}`); diff --git a/tests/utils/test-builders.ts b/tests/utils/test-builders.ts index 4a2cc03e2..9eebe60f5 100644 --- a/tests/utils/test-builders.ts +++ b/tests/utils/test-builders.ts @@ -298,6 +298,7 @@ interface TestMempoolTxArgs { smart_contract_clarity_version?: number; smart_contract_contract_id?: string; status?: DbTxStatus; + replacing_txid?: string, token_transfer_recipient_address?: string; token_transfer_amount?: bigint; token_transfer_memo?: string; @@ -326,6 +327,7 @@ export function testMempoolTx(args?: TestMempoolTxArgs): DbMempoolTxRaw { type_id: args?.type_id ?? DbTxTypeId.TokenTransfer, receipt_time: args?.receipt_time ?? (new Date().getTime() / 1000) | 0, status: args?.status ?? DbTxStatus.Pending, + replacing_txid: args?.replacing_txid, post_conditions: '0x01f5', fee_rate: args?.fee_rate ?? 1234n, sponsored: args?.sponsored ?? false,