Skip to content

Commit 1d566c0

Browse files
kiminuoluke-jr
authored andcommitted
test: Add mempool fee histogram test coverage
Original commit: bitcoin@0b6ba66 Co-authored-by: João Barbosa <[email protected]> Co-authored-by: Jon Atack <[email protected]> Github-Pull: bitcoin#21422 Rebased-From: c5e53d0
1 parent c756042 commit 1d566c0

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2023 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test mempool fee histogram."""
6+
7+
from decimal import Decimal
8+
9+
from test_framework.blocktools import COINBASE_MATURITY
10+
from test_framework.test_framework import BitcoinTestFramework
11+
from test_framework.util import (
12+
assert_equal,
13+
assert_greater_than,
14+
assert_greater_than_or_equal,
15+
)
16+
17+
class MempoolFeeHistogramTest(BitcoinTestFramework):
18+
def add_options(self, parser):
19+
self.add_wallet_options(parser)
20+
21+
def set_test_params(self):
22+
self.setup_clean_chain = True
23+
self.num_nodes = 1
24+
25+
def skip_test_if_missing_module(self):
26+
self.skip_if_no_wallet()
27+
28+
def run_test(self):
29+
node = self.nodes[0]
30+
self.generate(self.nodes[0], COINBASE_MATURITY + 2, sync_fun=self.no_op)
31+
32+
# We have two UTXOs (utxo_1 and utxo_2) and we create three changeless transactions:
33+
# - tx1 (5 sat/vB): spending utxo_1
34+
# - tx2 (14 sat/vB): spending output from tx1
35+
# - tx3 (6 sat/vB): spending utxo_2 and the output from tx2
36+
37+
self.log.info("Test getmempoolinfo does not return fee histogram by default")
38+
assert ("fee_histogram" not in node.getmempoolinfo())
39+
40+
self.log.info("Test getmempoolinfo returns empty fee histogram when mempool is empty")
41+
info = node.getmempoolinfo([1, 2, 3])
42+
43+
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
44+
assert_equal(0, non_empty_groups)
45+
assert_equal(3, empty_groups)
46+
assert_equal(0, total_fees)
47+
48+
for i in ['1', '2', '3']:
49+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['size'])
50+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['count'])
51+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['fees'])
52+
assert_equal(int(i), info['fee_histogram']['fee_rate_groups'][i]['from'])
53+
54+
self.log.info("Test that we have two spendable UTXOs and lock the second one")
55+
utxos = node.listunspent()
56+
assert_equal(2, len(utxos))
57+
node.lockunspent(False, [{"txid": utxos[1]["txid"], "vout": utxos[1]["vout"]}])
58+
59+
self.log.info("Send tx1 transaction with 5 sat/vB fee rate")
60+
node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("50.0"), fee_rate=5, subtractfeefromamount=True)
61+
62+
self.log.info("Test fee rate histogram when mempool contains 1 transaction (tx1: 5 sat/vB)")
63+
info = node.getmempoolinfo([1, 3, 5, 10])
64+
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
65+
assert_equal(1, non_empty_groups)
66+
assert_equal(3, empty_groups)
67+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count'])
68+
assert_equal(total_fees, info['fee_histogram']['total_fees'])
69+
70+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['size'])
71+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['count'])
72+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['fees'])
73+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['1']['from'])
74+
75+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['size'])
76+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['count'])
77+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['3']['fees'])
78+
assert_equal(3, info['fee_histogram']['fee_rate_groups']['3']['from'])
79+
80+
assert_equal(188, info['fee_histogram']['fee_rate_groups']['5']['size'])
81+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count'])
82+
assert_equal(940, info['fee_histogram']['fee_rate_groups']['5']['fees'])
83+
assert_equal(5, info['fee_histogram']['fee_rate_groups']['5']['from'])
84+
85+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['size'])
86+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['count'])
87+
assert_equal(0, info['fee_histogram']['fee_rate_groups']['10']['fees'])
88+
assert_equal(10, info['fee_histogram']['fee_rate_groups']['10']['from'])
89+
90+
self.log.info("Send tx2 transaction with 14 sat/vB fee rate (spends tx1 UTXO)")
91+
node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("25.0"), fee_rate=14, subtractfeefromamount=True)
92+
93+
self.log.info("Test fee rate histogram when mempool contains 2 transactions (tx1: 5 sat/vB, tx2: 14 sat/vB)")
94+
info = node.getmempoolinfo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
95+
96+
# Verify that tx1 and tx2 are reported in 5 sat/vB and 14 sat/vB in fee rate groups respectively
97+
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
98+
assert_equal(2, non_empty_groups)
99+
assert_equal(13, empty_groups)
100+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count'])
101+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['14']['count'])
102+
assert_equal(total_fees, info['fee_histogram']['total_fees'])
103+
104+
# Unlock the second UTXO which we locked
105+
node.lockunspent(True, [{"txid": utxos[1]["txid"], "vout": utxos[1]["vout"]}])
106+
107+
self.log.info("Send tx3 transaction with 6 sat/vB fee rate (spends all available UTXOs)")
108+
node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("99.9"), fee_rate=6, subtractfeefromamount=True)
109+
110+
self.log.info("Test fee rate histogram when mempool contains 3 transactions (tx1: 5 sat/vB, tx2: 14 sat/vB, tx3: 6 sat/vB)")
111+
info = node.getmempoolinfo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
112+
113+
# Verify that each of 5, 6 and 14 sat/vB fee rate groups contain one transaction
114+
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
115+
assert_equal(3, non_empty_groups)
116+
assert_equal(12, empty_groups)
117+
118+
for i in ['1', '2', '3', '4', '7', '8', '9', '10', '11', '12', '13', '15']:
119+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['size'])
120+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['count'])
121+
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['fees'])
122+
assert_equal(int(i), info['fee_histogram']['fee_rate_groups'][i]['from'])
123+
124+
assert_equal(188, info['fee_histogram']['fee_rate_groups']['5']['size'])
125+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count'])
126+
assert_equal(940, info['fee_histogram']['fee_rate_groups']['5']['fees'])
127+
assert_equal(5, info['fee_histogram']['fee_rate_groups']['5']['from'])
128+
129+
assert_equal(356, info['fee_histogram']['fee_rate_groups']['6']['size'])
130+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['6']['count'])
131+
assert_equal(2136, info['fee_histogram']['fee_rate_groups']['6']['fees'])
132+
assert_equal(6, info['fee_histogram']['fee_rate_groups']['6']['from'])
133+
134+
assert_equal(141, info['fee_histogram']['fee_rate_groups']['14']['size'])
135+
assert_equal(1, info['fee_histogram']['fee_rate_groups']['14']['count'])
136+
assert_equal(1974, info['fee_histogram']['fee_rate_groups']['14']['fees'])
137+
assert_equal(14, info['fee_histogram']['fee_rate_groups']['14']['from'])
138+
139+
assert_equal(total_fees, info['fee_histogram']['total_fees'])
140+
141+
def histogram_stats(self, histogram):
142+
total_fees = 0
143+
empty_count = 0
144+
non_empty_count = 0
145+
146+
for key, bin in histogram['fee_rate_groups'].items():
147+
assert_equal(int(key), bin['from'])
148+
if bin['fees'] > 0:
149+
assert_greater_than(bin['count'], 0)
150+
else:
151+
assert_equal(bin['count'], 0)
152+
assert_greater_than_or_equal(bin['fees'], 0)
153+
assert_greater_than_or_equal(bin['size'], 0)
154+
total_fees += bin['fees']
155+
156+
if bin['count'] == 0:
157+
empty_count += 1
158+
else:
159+
non_empty_count += 1
160+
161+
return (non_empty_count, empty_count, total_fees)
162+
163+
if __name__ == '__main__':
164+
MempoolFeeHistogramTest(__file__).main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@
304304
'p2p_initial_headers_sync.py',
305305
'feature_nulldummy.py',
306306
'mempool_accept.py',
307+
'mempool_fee_histogram.py',
307308
'mempool_expiry.py',
308309
'wallet_import_with_label.py --legacy-wallet',
309310
'wallet_importdescriptors.py --descriptors',

0 commit comments

Comments
 (0)