diff --git a/doc/tracing.md b/doc/tracing.md index 5b9369f0b91c0..184f6b010021c 100644 --- a/doc/tracing.md +++ b/doc/tracing.md @@ -245,14 +245,15 @@ Arguments passed: 2. Replaced transaction virtual size as `int32` 3. Replaced transaction fee as `int64` 4. Replaced transaction mempool entry time (epoch) as `uint64` -5. Replacement transaction ID (hash) as `pointer to unsigned chars` (i.e. 32 bytes in little-endian) +5. Replacement transaction ID or package hash as `pointer to unsigned chars` (i.e. 32 bytes in little-endian) 6. Replacement transaction virtual size as `int32` 7. Replacement transaction fee as `int64` +8. `bool` indicating if the argument 5. is a transaction ID or package hash (true if it's a transaction ID) -Note: In cases where a single replacement transaction replaces multiple +Note: In cases where a replacement transaction or package replaces multiple existing transactions in the mempool, the tracepoint is called once for each -replaced transaction, with data of the replacement transaction being the same -in each call. +replaced transaction, with data of the replacement transaction or package +being the same in each call. #### Tracepoint `mempool:rejected` diff --git a/src/validation.cpp b/src/validation.cpp index a76f56f1ef8d1..2e0ac29169545 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1299,7 +1299,8 @@ void MemPoolAccept::FinalizeSubpackage(const ATMPArgs& args) it->GetTxSize()); FeeFrac feerate{m_subpackage.m_total_modified_fees, int32_t(m_subpackage.m_total_vsize)}; uint256 tx_or_package_hash{}; - if (m_subpackage.m_changeset->GetTxCount() == 1) { + const bool replaced_with_tx{m_subpackage.m_changeset->GetTxCount() == 1}; + if (replaced_with_tx) { const CTransaction& tx = m_subpackage.m_changeset->GetAddedTxn(0); tx_or_package_hash = tx.GetHash(); log_string += strprintf("New tx %s (wtxid=%s, fees=%s, vsize=%s)", @@ -1324,7 +1325,8 @@ void MemPoolAccept::FinalizeSubpackage(const ATMPArgs& args) std::chrono::duration_cast>(it->GetTime()).count(), tx_or_package_hash.data(), feerate.size, - feerate.fee + feerate.fee, + replaced_with_tx ); m_subpackage.m_replaced_transactions.push_back(it->GetSharedTx()); } diff --git a/test/functional/interface_usdt_mempool.py b/test/functional/interface_usdt_mempool.py index a08827866569b..44882f642ea60 100755 --- a/test/functional/interface_usdt_mempool.py +++ b/test/functional/interface_usdt_mempool.py @@ -7,6 +7,7 @@ See https://github.com/bitcoin/bitcoin/blob/master/doc/tracing.md#context-mempool """ +import ctypes from decimal import Decimal # Test will be skipped if we don't have bcc installed @@ -63,6 +64,7 @@ u8 replacement_hash[HASH_LENGTH]; s32 replacement_vsize; s64 replacement_fee; + bool replaced_by_transaction; }; // BPF perf buffer to push the data to user space. @@ -115,6 +117,7 @@ bpf_usdt_readarg_p(5, ctx, &replaced.replacement_hash, HASH_LENGTH); bpf_usdt_readarg(6, ctx, &replaced.replacement_vsize); bpf_usdt_readarg(7, ctx, &replaced.replacement_fee); + bpf_usdt_readarg(8, ctx, &replaced.replaced_by_transaction); replaced_events.perf_submit(ctx, &replaced, sizeof(replaced)); return 0; @@ -123,6 +126,19 @@ """ +class MempoolReplaced(ctypes.Structure): + _fields_ = [ + ("replaced_hash", ctypes.c_ubyte * 32), + ("replaced_vsize", ctypes.c_int32), + ("replaced_fee", ctypes.c_int64), + ("replaced_entry_time", ctypes.c_uint64), + ("replacement_hash", ctypes.c_ubyte * 32), + ("replacement_vsize", ctypes.c_int32), + ("replacement_fee", ctypes.c_int64), + ("replaced_by_transaction", ctypes.c_bool), + ] + + class MempoolTracepointTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 @@ -230,7 +246,8 @@ def replaced_test(self): bpf = BPF(text=MEMPOOL_TRACEPOINTS_PROGRAM, usdt_contexts=[ctx], debug=0, cflags=["-Wno-error=implicit-function-declaration"]) def handle_replaced_event(_, data, __): - events.append(bpf["replaced_events"].event(data)) + event = ctypes.cast(data, ctypes.POINTER(MempoolReplaced)).contents + events.append(event) bpf["replaced_events"].open_perf_buffer(handle_replaced_event) @@ -261,6 +278,7 @@ def handle_replaced_event(_, data, __): assert_equal(bytes(event.replacement_hash)[::-1].hex(), replacement_tx["txid"]) assert_equal(event.replacement_vsize, replacement_tx["tx"].get_vsize()) assert_equal(event.replacement_fee, replacement_fee) + assert_equal(event.replaced_by_transaction, True) bpf.cleanup() self.generate(self.wallet, 1)