tracing: pass if replaced by tx/pkg to tracepoint

The mempool:replaced tracepoint now reports either a txid or a
package hash (previously it always was a txid). To let users know
if a txid or package hash is passed, a boolean argument is added
the the tracepoint.

In the functional test, a ctypes.Structure class for MempoolReplaced
is introduced as Python warns the following when not explcitly
casting it to a ctype:

  Type: 'bool' not recognized. Please define the data with ctypes manually.
This commit is contained in:
0xb10c 2024-11-07 14:53:06 +01:00 committed by Suhas Daftuar
parent a4ec07f194
commit 5736d1ddac
3 changed files with 28 additions and 7 deletions

View file

@ -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`

View file

@ -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<std::chrono::duration<std::uint64_t>>(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());
}

View file

@ -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 @@ struct replaced_event
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 @@ int trace_replaced(struct pt_regs *ctx) {
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 @@ int trace_replaced(struct pt_regs *ctx) {
"""
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 @@ class MempoolTracepointTest(BitcoinTestFramework):
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 @@ class MempoolTracepointTest(BitcoinTestFramework):
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)