44# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55"""Test fee estimation code."""
66from decimal import Decimal
7+ import os
78import random
89
910from test_framework .messages import (
@@ -158,6 +159,21 @@ def check_estimates(node, fees_seen):
158159 check_raw_estimates (node , fees_seen )
159160 check_smart_estimates (node , fees_seen )
160161
162+
163+ def send_tx (node , utxo , feerate ):
164+ """Broadcast a 1in-1out transaction with a specific input and feerate (sat/vb)."""
165+ overhead , op , scriptsig , nseq , value , spk = 10 , 36 , 5 , 4 , 8 , 24
166+ tx_size = overhead + op + scriptsig + nseq + value + spk
167+ fee = tx_size * feerate
168+
169+ tx = CTransaction ()
170+ tx .vin = [CTxIn (COutPoint (int (utxo ["txid" ], 16 ), utxo ["vout" ]), SCRIPT_SIG [utxo ["vout" ]])]
171+ tx .vout = [CTxOut (int (utxo ["amount" ] * COIN ) - fee , P2SH_1 )]
172+ txid = node .sendrawtransaction (tx .serialize ().hex ())
173+
174+ return txid
175+
176+
161177class EstimateFeeTest (BitcoinTestFramework ):
162178 def set_test_params (self ):
163179 self .num_nodes = 3
@@ -215,48 +231,36 @@ def transact_and_mine(self, numblocks, mining_node):
215231 newmem .append (utx )
216232 self .memutxo = newmem
217233
218- def run_test (self ):
219- self .log .info ("This test is time consuming, please be patient" )
220- self .log .info ("Splitting inputs so we can generate tx's" )
221-
222- # Start node0
223- self .start_node (0 )
234+ def initial_split (self , node ):
235+ """Split two coinbase UTxOs into many small coins"""
224236 self .txouts = []
225237 self .txouts2 = []
226238 # Split a coinbase into two transaction puzzle outputs
227- split_inputs (self . nodes [ 0 ], self . nodes [ 0 ] .listunspent (0 ), self .txouts , True )
239+ split_inputs (node , node .listunspent (0 ), self .txouts , True )
228240
229241 # Mine
230242 while len (self .nodes [0 ].getrawmempool ()) > 0 :
231- self .generate (self . nodes [ 0 ] , 1 , sync_fun = self .no_op )
243+ self .generate (node , 1 , sync_fun = self .no_op )
232244
233245 # Repeatedly split those 2 outputs, doubling twice for each rep
234246 # Use txouts to monitor the available utxo, since these won't be tracked in wallet
235247 reps = 0
236248 while reps < 5 :
237249 # Double txouts to txouts2
238250 while len (self .txouts ) > 0 :
239- split_inputs (self . nodes [ 0 ] , self .txouts , self .txouts2 )
240- while len (self . nodes [ 0 ] .getrawmempool ()) > 0 :
241- self .generate (self . nodes [ 0 ] , 1 , sync_fun = self .no_op )
251+ split_inputs (node , self .txouts , self .txouts2 )
252+ while len (node .getrawmempool ()) > 0 :
253+ self .generate (node , 1 , sync_fun = self .no_op )
242254 # Double txouts2 to txouts
243255 while len (self .txouts2 ) > 0 :
244- split_inputs (self . nodes [ 0 ] , self .txouts2 , self .txouts )
245- while len (self . nodes [ 0 ] .getrawmempool ()) > 0 :
246- self .generate (self . nodes [ 0 ] , 1 , sync_fun = self .no_op )
256+ split_inputs (node , self .txouts2 , self .txouts )
257+ while len (node .getrawmempool ()) > 0 :
258+ self .generate (node , 1 , sync_fun = self .no_op )
247259 reps += 1
248- self .log .info ("Finished splitting" )
249-
250- # Now we can connect the other nodes, didn't want to connect them earlier
251- # so the estimates would not be affected by the splitting transactions
252- self .start_node (1 )
253- self .start_node (2 )
254- self .connect_nodes (1 , 0 )
255- self .connect_nodes (0 , 2 )
256- self .connect_nodes (2 , 1 )
257-
258- self .sync_all ()
259260
261+ def sanity_check_estimates_range (self ):
262+ """Populate estimation buckets, assert estimates are in a sane range and
263+ are strictly increasing as the target decreases."""
260264 self .fees_per_kb = []
261265 self .memutxo = []
262266 self .confutxo = self .txouts # Start with the set of confirmed txouts after splitting
@@ -282,12 +286,48 @@ def run_test(self):
282286 self .log .info ("Final estimates after emptying mempools" )
283287 check_estimates (self .nodes [1 ], self .fees_per_kb )
284288
285- # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
286- self .log .info ("Test fee rate estimation after restarting node with high MempoolMinFee" )
289+ def test_feerate_mempoolminfee (self ):
287290 high_val = 3 * self .nodes [1 ].estimatesmartfee (1 )['feerate' ]
288291 self .restart_node (1 , extra_args = [f'-minrelaytxfee={ high_val } ' ])
289292 check_estimates (self .nodes [1 ], self .fees_per_kb )
290293 self .stop_node (1 , expected_stderr = "Warning: -minrelaytxfee is set very high! The wallet will avoid paying less than the minimum relay fee." )
294+ # TODO CHECK CHECK MAY BE THIS
295+ # self.restart_node(1)
296+
297+
298+ def run_test (self ):
299+ self .log .info ("This test is time consuming, please be patient" )
300+ self .log .info ("Splitting inputs so we can generate tx's" )
301+
302+ # Split two coinbases into many small utxos
303+ self .start_node (0 )
304+ self .initial_split (self .nodes [0 ])
305+ self .log .info ("Finished splitting" )
306+
307+ # Now we can connect the other nodes, didn't want to connect them earlier
308+ # so the estimates would not be affected by the splitting transactions
309+ self .start_node (1 )
310+ self .start_node (2 )
311+ self .connect_nodes (1 , 0 )
312+ self .connect_nodes (0 , 2 )
313+ self .connect_nodes (2 , 1 )
314+ self .sync_all ()
315+
316+ self .log .info ("Testing estimates with single transactions." )
317+ self .sanity_check_estimates_range ()
318+
319+ # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
320+ self .log .info ("Test fee rate estimation after restarting node with high MempoolMinFee" )
321+ self .test_feerate_mempoolminfee ()
322+
323+ self .log .info ("Restarting node with fresh estimation" )
324+ self .stop_node (0 )
325+ fee_dat = os .path .join (self .nodes [0 ].datadir , self .chain , "fee_estimates.dat" )
326+ os .remove (fee_dat )
327+ self .start_node (0 )
328+ self .connect_nodes (0 , 1 )
329+ self .connect_nodes (0 , 2 )
330+
291331
292332 self .log .info ("Testing that fee estimation is disabled in blocksonly." )
293333 self .restart_node (0 , ["-blocksonly" ])
0 commit comments