7
7
from decimal import Decimal
8
8
9
9
from test_framework .blocktools import COINBASE_MATURITY
10
+ from test_framework .messages import (
11
+ COIN ,
12
+ )
10
13
from test_framework .test_framework import BitcoinTestFramework
11
14
from test_framework .util import (
12
15
assert_equal ,
13
16
assert_greater_than ,
14
17
assert_greater_than_or_equal ,
18
+ ceildiv ,
15
19
)
16
20
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
+
17
32
class MempoolFeeHistogramTest (BitcoinTestFramework ):
18
33
def add_options (self , parser ):
19
34
self .add_wallet_options (parser )
@@ -57,14 +72,15 @@ def run_test(self):
57
72
node .lockunspent (False , [{"txid" : utxos [1 ]["txid" ], "vout" : utxos [1 ]["vout" ]}])
58
73
59
74
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 )
61
77
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)" )
63
79
info = node .getmempoolinfo ([1 , 3 , 5 , 10 ])
64
80
(non_empty_groups , empty_groups , total_fees ) = self .histogram_stats (info ['fee_histogram' ])
65
81
assert_equal (1 , non_empty_groups )
66
82
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' ])
68
84
assert_equal (total_fees , info ['fee_histogram' ]['total_fees' ])
69
85
70
86
assert_equal (0 , info ['fee_histogram' ]['fee_rate_groups' ]['1' ]['size' ])
@@ -88,54 +104,57 @@ def run_test(self):
88
104
assert_equal (10 , info ['fee_histogram' ]['fee_rate_groups' ]['10' ]['from' ])
89
105
90
106
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 )
92
109
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)" )
94
111
info = node .getmempoolinfo ([1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ])
95
112
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
97
114
(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' ])
98
116
assert_equal (1 , non_empty_groups )
99
117
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' ])
101
119
assert_equal (total_fees , info ['fee_histogram' ]['total_fees' ])
102
120
103
121
# Unlock the second UTXO which we locked
104
122
node .lockunspent (True , [{"txid" : utxos [1 ]["txid" ], "vout" : utxos [1 ]["vout" ]}])
105
123
106
124
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 )
108
127
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)" )
110
129
info = node .getmempoolinfo ([1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ])
111
130
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' ])
139
158
140
159
def histogram_stats (self , histogram ):
141
160
total_fees = 0
0 commit comments