4040REDEEM_SCRIPT = CScript ([OP_TRUE , SCRIPT ])
4141
4242
43- def small_txpuzzle_randfee (from_node , conflist , unconflist , amount , min_fee , fee_increment ):
43+ def small_txpuzzle_randfee (
44+ from_node , conflist , unconflist , amount , min_fee , fee_increment
45+ ):
4446 """Create and send a transaction with a random fee.
4547
4648 The transaction pays to a trivial P2SH script, and assumes that its inputs
@@ -81,34 +83,6 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
8183 return (tx .serialize ().hex (), fee )
8284
8385
84- def split_inputs (from_node , txins , txouts , initial_split = False ):
85- """Generate a lot of inputs so we can generate a ton of transactions.
86-
87- This function takes an input from txins, and creates and sends a transaction
88- which splits the value into 2 outputs which are appended to txouts.
89- Previously this was designed to be small inputs so they wouldn't have
90- a high coin age when the notion of priority still existed."""
91-
92- prevtxout = txins .pop ()
93- tx = CTransaction ()
94- tx .vin .append (CTxIn (COutPoint (int (prevtxout ["txid" ], 16 ), prevtxout ["vout" ]), b"" ))
95-
96- half_change = satoshi_round (prevtxout ["amount" ] / 2 )
97- rem_change = prevtxout ["amount" ] - half_change - Decimal ("0.00001000" )
98- tx .vout .append (CTxOut (int (half_change * COIN ), P2SH ))
99- tx .vout .append (CTxOut (int (rem_change * COIN ), P2SH ))
100-
101- # If this is the initial split we actually need to sign the transaction
102- # Otherwise we just need to insert the proper ScriptSig
103- if (initial_split ):
104- completetx = from_node .signrawtransactionwithwallet (tx .serialize ().hex ())["hex" ]
105- else :
106- tx .vin [0 ].scriptSig = REDEEM_SCRIPT
107- completetx = tx .serialize ().hex ()
108- txid = from_node .sendrawtransaction (hexstring = completetx , maxfeerate = 0 )
109- txouts .append ({"txid" : txid , "vout" : 0 , "amount" : half_change })
110- txouts .append ({"txid" : txid , "vout" : 1 , "amount" : rem_change })
111-
11286def check_raw_estimates (node , fees_seen ):
11387 """Call estimaterawfee and verify that the estimates meet certain invariants."""
11488
@@ -119,33 +93,41 @@ def check_raw_estimates(node, fees_seen):
11993 assert_greater_than (feerate , 0 )
12094
12195 if feerate + delta < min (fees_seen ) or feerate - delta > max (fees_seen ):
122- raise AssertionError (f"Estimated fee ({ feerate } ) out of range ({ min (fees_seen )} ,{ max (fees_seen )} )" )
96+ raise AssertionError (
97+ f"Estimated fee ({ feerate } ) out of range ({ min (fees_seen )} ,{ max (fees_seen )} )"
98+ )
99+
123100
124101def check_smart_estimates (node , fees_seen ):
125102 """Call estimatesmartfee and verify that the estimates meet certain invariants."""
126103
127104 delta = 1.0e-6 # account for rounding error
128105 last_feerate = float (max (fees_seen ))
129106 all_smart_estimates = [node .estimatesmartfee (i ) for i in range (1 , 26 )]
130- mempoolMinFee = node .getmempoolinfo ()[' mempoolminfee' ]
131- minRelaytxFee = node .getmempoolinfo ()[' minrelaytxfee' ]
107+ mempoolMinFee = node .getmempoolinfo ()[" mempoolminfee" ]
108+ minRelaytxFee = node .getmempoolinfo ()[" minrelaytxfee" ]
132109 for i , e in enumerate (all_smart_estimates ): # estimate is for i+1
133110 feerate = float (e ["feerate" ])
134111 assert_greater_than (feerate , 0 )
135112 assert_greater_than_or_equal (feerate , float (mempoolMinFee ))
136113 assert_greater_than_or_equal (feerate , float (minRelaytxFee ))
137114
138115 if feerate + delta < min (fees_seen ) or feerate - delta > max (fees_seen ):
139- raise AssertionError (f"Estimated fee ({ feerate } ) out of range ({ min (fees_seen )} ,{ max (fees_seen )} )" )
116+ raise AssertionError (
117+ f"Estimated fee ({ feerate } ) out of range ({ min (fees_seen )} ,{ max (fees_seen )} )"
118+ )
140119 if feerate - delta > last_feerate :
141- raise AssertionError (f"Estimated fee ({ feerate } ) larger than last fee ({ last_feerate } ) for lower number of confirms" )
120+ raise AssertionError (
121+ f"Estimated fee ({ feerate } ) larger than last fee ({ last_feerate } ) for lower number of confirms"
122+ )
142123 last_feerate = feerate
143124
144125 if i == 0 :
145126 assert_equal (e ["blocks" ], 2 )
146127 else :
147128 assert_greater_than_or_equal (i + 1 , e ["blocks" ])
148129
130+
149131def check_estimates (node , fees_seen ):
150132 check_raw_estimates (node , fees_seen )
151133 check_smart_estimates (node , fees_seen )
@@ -206,11 +188,17 @@ def transact_and_mine(self, numblocks, mining_node):
206188 random .shuffle (self .confutxo )
207189 for _ in range (random .randrange (100 - 50 , 100 + 50 )):
208190 from_index = random .randint (1 , 2 )
209- (txhex , fee ) = small_txpuzzle_randfee (self .nodes [from_index ], self .confutxo ,
210- self .memutxo , Decimal ("0.005" ), min_fee , min_fee )
191+ (txhex , fee ) = small_txpuzzle_randfee (
192+ self .nodes [from_index ],
193+ self .confutxo ,
194+ self .memutxo ,
195+ Decimal ("0.005" ),
196+ min_fee ,
197+ min_fee ,
198+ )
211199 tx_kbytes = (len (txhex ) // 2 ) / 1000.0
212200 self .fees_per_kb .append (float (fee ) / tx_kbytes )
213- self .sync_mempools (wait = .1 )
201+ self .sync_mempools (wait = 0 .1 )
214202 mined = mining_node .getblock (self .generate (mining_node , 1 )[0 ], True )["tx" ]
215203 # update which txouts are confirmed
216204 newmem = []
@@ -223,46 +211,45 @@ def transact_and_mine(self, numblocks, mining_node):
223211
224212 def initial_split (self , node ):
225213 """Split two coinbase UTxOs into many small coins"""
226- self .txouts = []
227- self .txouts2 = []
228- # Split a coinbase into two transaction puzzle outputs
229- split_inputs (node , node .listunspent (0 ), self .txouts , True )
230-
231- # Mine
214+ utxo_count = 2048
215+ self .confutxo = []
216+ splitted_amount = Decimal ("0.04" )
217+ fee = Decimal ("0.1" )
218+ change = Decimal ("100" ) - splitted_amount * utxo_count - fee
219+ tx = CTransaction ()
220+ tx .vin = [
221+ CTxIn (COutPoint (int (cb ["txid" ], 16 ), cb ["vout" ]), b"" )
222+ for cb in node .listunspent ()[:2 ]
223+ ]
224+ tx .vout = [CTxOut (int (splitted_amount * COIN ), P2SH ) for _ in range (utxo_count )]
225+ tx .vout .append (CTxOut (int (change * COIN ), P2SH ))
226+ txhex = node .signrawtransactionwithwallet (tx .serialize ().hex ())["hex" ]
227+ txid = node .sendrawtransaction (txhex )
228+ self .confutxo = [
229+ {"txid" : txid , "vout" : i , "amount" : splitted_amount }
230+ for i in range (utxo_count )
231+ ]
232232 while len (node .getrawmempool ()) > 0 :
233233 self .generate (node , 1 , sync_fun = self .no_op )
234234
235- # Repeatedly split those 2 outputs, doubling twice for each rep
236- # Use txouts to monitor the available utxo, since these won't be tracked in wallet
237- reps = 0
238- while reps < 5 :
239- # Double txouts to txouts2
240- while len (self .txouts ) > 0 :
241- split_inputs (node , self .txouts , self .txouts2 )
242- while len (node .getrawmempool ()) > 0 :
243- self .generate (node , 1 , sync_fun = self .no_op )
244- # Double txouts2 to txouts
245- while len (self .txouts2 ) > 0 :
246- split_inputs (node , self .txouts2 , self .txouts )
247- while len (node .getrawmempool ()) > 0 :
248- self .generate (node , 1 , sync_fun = self .no_op )
249- reps += 1
250-
251235 def sanity_check_estimates_range (self ):
252236 """Populate estimation buckets, assert estimates are in a sane range and
253237 are strictly increasing as the target decreases."""
254238 self .fees_per_kb = []
255239 self .memutxo = []
256- self .confutxo = self .txouts # Start with the set of confirmed txouts after splitting
257240 self .log .info ("Will output estimates for 1/2/3/6/15/25 blocks" )
258241
259242 for _ in range (2 ):
260- self .log .info ("Creating transactions and mining them with a block size that can't keep up" )
243+ self .log .info (
244+ "Creating transactions and mining them with a block size that can't keep up"
245+ )
261246 # Create transactions and mine 10 small blocks with node 2, but create txs faster than we can mine
262247 self .transact_and_mine (10 , self .nodes [2 ])
263248 check_estimates (self .nodes [1 ], self .fees_per_kb )
264249
265- self .log .info ("Creating transactions and mining them at a block size that is just big enough" )
250+ self .log .info (
251+ "Creating transactions and mining them at a block size that is just big enough"
252+ )
266253 # Generate transactions while mining 10 more blocks, this time with node1
267254 # which mines blocks with capacity just above the rate that transactions are being created
268255 self .transact_and_mine (10 , self .nodes [1 ])
@@ -271,12 +258,13 @@ def sanity_check_estimates_range(self):
271258 # Finish by mining a normal-sized block:
272259 while len (self .nodes [1 ].getrawmempool ()) > 0 :
273260 self .generate (self .nodes [1 ], 1 )
261+
274262 self .log .info ("Final estimates after emptying mempools" )
275263 check_estimates (self .nodes [1 ], self .fees_per_kb )
276264
277265 def test_feerate_mempoolminfee (self ):
278- high_val = 3 * self .nodes [1 ].estimatesmartfee (1 )[' feerate' ]
279- self .restart_node (1 , extra_args = [f' -minrelaytxfee={ high_val } ' ])
266+ high_val = 3 * self .nodes [1 ].estimatesmartfee (1 )[" feerate" ]
267+ self .restart_node (1 , extra_args = [f" -minrelaytxfee={ high_val } " ])
280268 check_estimates (self .nodes [1 ], self .fees_per_kb )
281269 self .restart_node (1 )
282270
@@ -309,7 +297,7 @@ def sanity_check_rbf_estimates(self, utxos):
309297 for _ in range (5 ):
310298 send_tx (node , utxos .pop (0 ), low_feerate )
311299 # Mine the transactions on another node
312- self .sync_mempools (wait = .1 , nodes = [node , miner ])
300+ self .sync_mempools (wait = 0 .1 , nodes = [node , miner ])
313301 for txid in txids_to_replace :
314302 miner .prioritisetransaction (txid = txid , fee_delta = - COIN )
315303 self .generate (miner , 1 )
@@ -322,12 +310,12 @@ def sanity_check_rbf_estimates(self, utxos):
322310 break
323311
324312 # Mine the last replacement txs
325- self .sync_mempools (wait = .1 , nodes = [node , miner ])
313+ self .sync_mempools (wait = 0 .1 , nodes = [node , miner ])
326314 self .generate (miner , 1 )
327315
328316 # Only 10% of the transactions were really confirmed with a low feerate,
329317 # the rest needed to be RBF'd. We must return the 90% conf rate feerate.
330- high_feerate_kvb = Decimal (high_feerate ) / COIN * 10 ** 3
318+ high_feerate_kvb = Decimal (high_feerate ) / COIN * 10 ** 3
331319 est_feerate = node .estimatesmartfee (2 )["feerate" ]
332320 assert est_feerate == high_feerate_kvb
333321
@@ -353,7 +341,9 @@ def run_test(self):
353341 self .sanity_check_estimates_range ()
354342
355343 # check that the effective feerate is greater than or equal to the mempoolminfee even for high mempoolminfee
356- self .log .info ("Test fee rate estimation after restarting node with high MempoolMinFee" )
344+ self .log .info (
345+ "Test fee rate estimation after restarting node with high MempoolMinFee"
346+ )
357347 self .test_feerate_mempoolminfee ()
358348
359349 self .log .info ("Restarting node with fresh estimation" )
@@ -369,9 +359,10 @@ def run_test(self):
369359
370360 self .log .info ("Testing that fee estimation is disabled in blocksonly." )
371361 self .restart_node (0 , ["-blocksonly" ])
372- assert_raises_rpc_error (- 32603 , "Fee estimation disabled" ,
373- self .nodes [0 ].estimatesmartfee , 2 )
362+ assert_raises_rpc_error (
363+ - 32603 , "Fee estimation disabled" , self .nodes [0 ].estimatesmartfee , 2
364+ )
374365
375366
376- if __name__ == ' __main__' :
367+ if __name__ == " __main__" :
377368 EstimateFeeTest ().main ()
0 commit comments