Skip to content

Commit eb93c8b

Browse files
committed
Bugfix: QA: Ensure mempool_fee_histogram can adapt to feerate rounding correctly
Caution: This implementation is for a post-bitcoin#22949 codebase
1 parent ecd567a commit eb93c8b

File tree

1 file changed

+55
-36
lines changed

1 file changed

+55
-36
lines changed

test/functional/mempool_fee_histogram.py

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,28 @@
77
from decimal import Decimal
88

99
from test_framework.blocktools import COINBASE_MATURITY
10+
from test_framework.messages import (
11+
COIN,
12+
)
1013
from test_framework.test_framework import BitcoinTestFramework
1114
from test_framework.util import (
1215
assert_equal,
1316
assert_greater_than,
1417
assert_greater_than_or_equal,
18+
ceildiv,
1519
)
1620

21+
def get_actual_fee_rate(fee_in_satoshis, vsize):
22+
fee_rate = ceildiv(fee_in_satoshis, vsize)
23+
return str(fee_rate)
24+
25+
def get_tx_details(node, txid):
26+
info = node.gettransaction(txid=txid)
27+
info.update(node.getrawtransaction(txid=txid, verbose=True))
28+
info['fee'] = int(-info['fee'] * COIN) # convert to satoshis
29+
info['feerate'] = get_actual_fee_rate(info['fee'], info['vsize'])
30+
return info
31+
1732
class MempoolFeeHistogramTest(BitcoinTestFramework):
1833
def add_options(self, parser):
1934
self.add_wallet_options(parser)
@@ -57,14 +72,15 @@ def run_test(self):
5772
node.lockunspent(False, [{"txid": utxos[1]["txid"], "vout": utxos[1]["vout"]}])
5873

5974
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)
75+
tx1_txid = node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("50.0"), fee_rate=5, subtractfeefromamount=True)
76+
tx1_info = get_tx_details(node, tx1_txid)
6177

62-
self.log.info("Test fee rate histogram when mempool contains 1 transaction (tx1: 5 sat/vB)")
78+
self.log.info(f"Test fee rate histogram when mempool contains 1 transaction (tx1: {tx1_info['feerate']} sat/vB)")
6379
info = node.getmempoolinfo([1, 3, 5, 10])
6480
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
6581
assert_equal(1, non_empty_groups)
6682
assert_equal(3, empty_groups)
67-
assert_equal(1, info['fee_histogram']['fee_rate_groups']['5']['count'])
83+
assert_equal(1, info['fee_histogram']['fee_rate_groups'][tx1_info['feerate']]['count'])
6884
assert_equal(total_fees, info['fee_histogram']['total_fees'])
6985

7086
assert_equal(0, info['fee_histogram']['fee_rate_groups']['1']['size'])
@@ -88,54 +104,57 @@ def run_test(self):
88104
assert_equal(10, info['fee_histogram']['fee_rate_groups']['10']['from'])
89105

90106
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)
107+
tx2_txid = node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("25.0"), fee_rate=14, subtractfeefromamount=True)
108+
tx2_info = get_tx_details(node, tx2_txid)
92109

93-
self.log.info("Test fee rate histogram when mempool contains 2 transactions (tx1: 5 sat/vB, tx2: 14 sat/vB)")
110+
self.log.info(f"Test fee rate histogram when mempool contains 2 transactions (tx1: {tx1_info['feerate']} sat/vB, tx2: {tx2_info['feerate']} sat/vB)")
94111
info = node.getmempoolinfo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
95112

96-
# Verify that both tx1 and tx2 are reported in 8 sat/vB fee rate group
113+
# Verify that both tx1 and tx2 are reported in 9 sat/vB fee rate group
97114
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
115+
tx1p2_feerate = get_actual_fee_rate(tx1_info['fee'] + tx2_info['fee'], tx1_info['vsize'] + tx2_info['vsize'])
98116
assert_equal(1, non_empty_groups)
99117
assert_equal(14, empty_groups)
100-
assert_equal(2, info['fee_histogram']['fee_rate_groups']['8']['count'])
118+
assert_equal(2, info['fee_histogram']['fee_rate_groups'][tx1p2_feerate]['count'])
101119
assert_equal(total_fees, info['fee_histogram']['total_fees'])
102120

103121
# Unlock the second UTXO which we locked
104122
node.lockunspent(True, [{"txid": utxos[1]["txid"], "vout": utxos[1]["vout"]}])
105123

106124
self.log.info("Send tx3 transaction with 6 sat/vB fee rate (spends all available UTXOs)")
107-
node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("99.9"), fee_rate=6, subtractfeefromamount=True)
125+
tx3_txid = node.sendtoaddress(address=node.getnewaddress(), amount=Decimal("99.9"), fee_rate=6, subtractfeefromamount=True)
126+
tx3_info = get_tx_details(node, tx3_txid)
108127

109-
self.log.info("Test fee rate histogram when mempool contains 3 transactions (tx1: 5 sat/vB, tx2: 14 sat/vB, tx3: 6 sat/vB)")
128+
self.log.info(f"Test fee rate histogram when mempool contains 3 transactions (tx1: {tx1_info['feerate']} sat/vB, tx2: {tx2_info['feerate']} sat/vB, tx3: {tx3_info['feerate']} sat/vB)")
110129
info = node.getmempoolinfo([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
111130

112-
# Verify that each of 6, 7 and 8 sat/vB fee rate groups contain one transaction
113-
(non_empty_groups, empty_groups, total_fees) = self.histogram_stats(info['fee_histogram'])
114-
assert_equal(3, non_empty_groups)
115-
assert_equal(12, empty_groups)
116-
117-
for i in ['1', '2', '3', '4', '5', '9', '10', '11', '12', '13', '14', '15']:
118-
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['size'])
119-
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['count'])
120-
assert_equal(0, info['fee_histogram']['fee_rate_groups'][i]['fees'])
121-
assert_equal(int(i), info['fee_histogram']['fee_rate_groups'][i]['from'])
122-
123-
assert_equal(188, info['fee_histogram']['fee_rate_groups']['6']['size'])
124-
assert_equal(1, info['fee_histogram']['fee_rate_groups']['6']['count'])
125-
assert_equal(940, info['fee_histogram']['fee_rate_groups']['6']['fees'])
126-
assert_equal(5, info['fee_histogram']['fee_rate_groups']['6']['from'])
127-
128-
assert_equal(356, info['fee_histogram']['fee_rate_groups']['7']['size'])
129-
assert_equal(1, info['fee_histogram']['fee_rate_groups']['7']['count'])
130-
assert_equal(2136, info['fee_histogram']['fee_rate_groups']['7']['fees'])
131-
assert_equal(6, info['fee_histogram']['fee_rate_groups']['7']['from'])
132-
133-
assert_equal(141, info['fee_histogram']['fee_rate_groups']['8']['size'])
134-
assert_equal(1, info['fee_histogram']['fee_rate_groups']['8']['count'])
135-
assert_equal(1974, info['fee_histogram']['fee_rate_groups']['8']['fees'])
136-
assert_equal(14, info['fee_histogram']['fee_rate_groups']['8']['from'])
137-
138-
assert_equal(total_fees, info['fee_histogram']['total_fees'])
131+
# Verify that each of 6, 8 and 9 sat/vB fee rate groups contain one transaction
132+
# tx1 should be grouped with tx2 + tx3 (descendants)
133+
# tx2 should be grouped with tx1 (ancestors only)
134+
# tx3 should be alone
135+
expected_histogram = {
136+
'fee_rate_groups': dict( (
137+
(str(n), {
138+
'from': n,
139+
'count': 0,
140+
'fees': 0,
141+
'size': 0,
142+
}) for n in range(1, 16)
143+
) ),
144+
'total_fees': tx1_info['fee'] + tx2_info['fee'] + tx3_info['fee'],
145+
}
146+
expected_frg = expected_histogram['fee_rate_groups']
147+
tx1p2p3_feerate = get_actual_fee_rate(expected_histogram['total_fees'], tx1_info['vsize'] + tx2_info['vsize'] + tx3_info['vsize'])
148+
def inc_expected(feerate, txinfo):
149+
this_frg = expected_frg[feerate]
150+
this_frg['count'] += 1
151+
this_frg['fees'] += txinfo['fee']
152+
this_frg['size'] += txinfo['vsize']
153+
inc_expected(tx1p2p3_feerate, tx1_info)
154+
inc_expected(tx1p2_feerate, tx2_info)
155+
inc_expected(tx3_info['feerate'], tx3_info)
156+
157+
assert_equal(expected_histogram, info['fee_histogram'])
139158

140159
def histogram_stats(self, histogram):
141160
total_fees = 0

0 commit comments

Comments
 (0)