33# Distributed under the MIT software license, see the accompanying
44# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55"""Test fee estimation code."""
6+ from decimal import Decimal
7+ import random
68
7- from test_framework .test_framework import BitcoinTestFramework
8- from test_framework .util import *
9- from test_framework .script import CScript , OP_1 , OP_DROP , OP_2 , OP_HASH160 , OP_EQUAL , hash160 , OP_TRUE
109from test_framework .mininode import CTransaction , CTxIn , CTxOut , COutPoint , ToHex , COIN
10+ from test_framework .script import CScript , OP_1 , OP_DROP , OP_2 , OP_HASH160 , OP_EQUAL , hash160 , OP_TRUE
11+ from test_framework .test_framework import BitcoinTestFramework
12+ from test_framework .util import satoshi_round , sync_mempools , sync_blocks , connect_nodes , assert_greater_than
1113
1214# Construct 2 trivial P2SH's and the ScriptSigs that spend them
1315# So we can create many transactions without needing to spend
1416# time signing.
15- redeem_script_1 = CScript ([OP_1 , OP_DROP ])
16- redeem_script_2 = CScript ([OP_2 , OP_DROP ])
17- P2SH_1 = CScript ([OP_HASH160 , hash160 (redeem_script_1 ), OP_EQUAL ])
18- P2SH_2 = CScript ([OP_HASH160 , hash160 (redeem_script_2 ), OP_EQUAL ])
17+ REDEEM_SCRIPT_1 = CScript ([OP_1 , OP_DROP ])
18+ REDEEM_SCRIPT_2 = CScript ([OP_2 , OP_DROP ])
19+ P2SH_1 = CScript ([OP_HASH160 , hash160 (REDEEM_SCRIPT_1 ), OP_EQUAL ])
20+ P2SH_2 = CScript ([OP_HASH160 , hash160 (REDEEM_SCRIPT_2 ), OP_EQUAL ])
1921
2022# Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2
21- SCRIPT_SIG = [CScript ([OP_TRUE , redeem_script_1 ]), CScript ([OP_TRUE , redeem_script_2 ])]
23+ SCRIPT_SIG = [CScript ([OP_TRUE , REDEEM_SCRIPT_1 ]), CScript ([OP_TRUE , REDEEM_SCRIPT_2 ])]
2224
2325global log
2426
2527def small_txpuzzle_randfee (from_node , conflist , unconflist , amount , min_fee , fee_increment ):
26- """
27- Create and send a transaction with a random fee.
28+ """Create and send a transaction with a random fee.
29+
2830 The transaction pays to a trivial P2SH script, and assumes that its inputs
2931 are of the same form.
3032 The function takes a list of confirmed outputs and unconfirmed outputs
3133 and attempts to use the confirmed list first for its inputs.
3234 It adds the newly created outputs to the unconfirmed list.
33- Returns (raw transaction, fee)
34- """
35+ Returns (raw transaction, fee)."""
36+
3537 # It's best to exponentially distribute our random fees
3638 # because the buckets are exponentially spaced.
3739 # Exponentially distributed from 1-128 * fee_increment
38- rand_fee = float (fee_increment )* (1.1892 ** random .randint (0 ,28 ))
40+ rand_fee = float (fee_increment ) * (1.1892 ** random .randint (0 , 28 ))
3941 # Total fee ranges from min_fee to min_fee + 127*fee_increment
4042 fee = min_fee - fee_increment + satoshi_round (rand_fee )
4143 tx = CTransaction ()
@@ -50,83 +52,81 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
5052 total_in += t ["amount" ]
5153 tx .vin .append (CTxIn (COutPoint (int (t ["txid" ], 16 ), t ["vout" ]), b"" ))
5254 if total_in <= amount + fee :
53- raise RuntimeError ("Insufficient funds: need %d, have %d" % (amount + fee , total_in ))
54- tx .vout .append (CTxOut (int ((total_in - amount - fee )* COIN ), P2SH_1 ))
55- tx .vout .append (CTxOut (int (amount * COIN ), P2SH_2 ))
55+ raise RuntimeError ("Insufficient funds: need %d, have %d" % (amount + fee , total_in ))
56+ tx .vout .append (CTxOut (int ((total_in - amount - fee ) * COIN ), P2SH_1 ))
57+ tx .vout .append (CTxOut (int (amount * COIN ), P2SH_2 ))
5658 # These transactions don't need to be signed, but we still have to insert
5759 # the ScriptSig that will satisfy the ScriptPubKey.
5860 for inp in tx .vin :
5961 inp .scriptSig = SCRIPT_SIG [inp .prevout .n ]
6062 txid = from_node .sendrawtransaction (ToHex (tx ), True )
61- unconflist .append ({ "txid" : txid , "vout" : 0 , "amount" : total_in - amount - fee })
62- unconflist .append ({ "txid" : txid , "vout" : 1 , "amount" : amount })
63+ unconflist .append ({"txid" : txid , "vout" : 0 , "amount" : total_in - amount - fee })
64+ unconflist .append ({"txid" : txid , "vout" : 1 , "amount" : amount })
6365
6466 return (ToHex (tx ), fee )
6567
66- def split_inputs (from_node , txins , txouts , initial_split = False ):
67- """
68- We need to generate a lot of inputs so we can generate a ton of transactions.
68+ def split_inputs (from_node , txins , txouts , initial_split = False ):
69+ """Generate a lot of inputs so we can generate a ton of transactions.
70+
6971 This function takes an input from txins, and creates and sends a transaction
7072 which splits the value into 2 outputs which are appended to txouts.
7173 Previously this was designed to be small inputs so they wouldn't have
72- a high coin age when the notion of priority still existed.
73- """
74+ a high coin age when the notion of priority still existed."""
75+
7476 prevtxout = txins .pop ()
7577 tx = CTransaction ()
7678 tx .vin .append (CTxIn (COutPoint (int (prevtxout ["txid" ], 16 ), prevtxout ["vout" ]), b"" ))
7779
78- half_change = satoshi_round (prevtxout ["amount" ]/ 2 )
79- rem_change = prevtxout ["amount" ] - half_change - Decimal ("0.00001000" )
80- tx .vout .append (CTxOut (int (half_change * COIN ), P2SH_1 ))
81- tx .vout .append (CTxOut (int (rem_change * COIN ), P2SH_2 ))
80+ half_change = satoshi_round (prevtxout ["amount" ] / 2 )
81+ rem_change = prevtxout ["amount" ] - half_change - Decimal ("0.00001000" )
82+ tx .vout .append (CTxOut (int (half_change * COIN ), P2SH_1 ))
83+ tx .vout .append (CTxOut (int (rem_change * COIN ), P2SH_2 ))
8284
8385 # If this is the initial split we actually need to sign the transaction
8486 # Otherwise we just need to insert the proper ScriptSig
85- if (initial_split ) :
87+ if (initial_split ):
8688 completetx = from_node .signrawtransaction (ToHex (tx ))["hex" ]
87- else :
89+ else :
8890 tx .vin [0 ].scriptSig = SCRIPT_SIG [prevtxout ["vout" ]]
8991 completetx = ToHex (tx )
9092 txid = from_node .sendrawtransaction (completetx , True )
91- txouts .append ({ "txid" : txid , "vout" : 0 , "amount" : half_change })
92- txouts .append ({ "txid" : txid , "vout" : 1 , "amount" : rem_change })
93-
94- def check_estimates (node , fees_seen , max_invalid , print_estimates = True ):
95- """
96- This function calls estimatefee and verifies that the estimates
97- meet certain invariants.
98- """
99- all_estimates = [ node .estimatefee (i ) for i in range (1 ,26 ) ]
93+ txouts .append ({"txid" : txid , "vout" : 0 , "amount" : half_change })
94+ txouts .append ({"txid" : txid , "vout" : 1 , "amount" : rem_change })
95+
96+ def check_estimates (node , fees_seen , max_invalid , print_estimates = True ):
97+ """Call estimatefee and verify that the estimates meet certain invariants."""
98+
99+ all_estimates = [node .estimatefee (i ) for i in range (1 , 26 )]
100100 if print_estimates :
101- log .info ([str (all_estimates [e - 1 ]) for e in [1 ,2 , 3 , 6 , 15 ,25 ]])
102- delta = 1.0e-6 # account for rounding error
101+ log .info ([str (all_estimates [e - 1 ]) for e in [1 , 2 , 3 , 6 , 15 , 25 ]])
102+ delta = 1.0e-6 # account for rounding error
103103 last_e = max (fees_seen )
104104 for e in [x for x in all_estimates if x >= 0 ]:
105105 # Estimates should be within the bounds of what transactions fees actually were:
106- if float (e )+ delta < min (fees_seen ) or float (e )- delta > max (fees_seen ):
106+ if float (e ) + delta < min (fees_seen ) or float (e ) - delta > max (fees_seen ):
107107 raise AssertionError ("Estimated fee (%f) out of range (%f,%f)"
108- % (float (e ), min (fees_seen ), max (fees_seen )))
108+ % (float (e ), min (fees_seen ), max (fees_seen )))
109109 # Estimates should be monotonically decreasing
110- if float (e )- delta > last_e :
110+ if float (e ) - delta > last_e :
111111 raise AssertionError ("Estimated fee (%f) larger than last fee (%f) for lower number of confirms"
112- % (float (e ),float (last_e )))
112+ % (float (e ), float (last_e )))
113113 last_e = e
114114 valid_estimate = False
115115 invalid_estimates = 0
116- for i ,e in enumerate (all_estimates ): # estimate is for i+1
116+ for i , e in enumerate (all_estimates ): # estimate is for i+1
117117 if e >= 0 :
118118 valid_estimate = True
119119 if i >= 13 : # for n>=14 estimatesmartfee(n/2) should be at least as high as estimatefee(n)
120- assert (node .estimatesmartfee ((i + 1 ) // 2 )["feerate" ] > float (e ) - delta )
120+ assert_greater_than (node .estimatesmartfee ((i + 1 ) // 2 )["feerate" ], float (e ) - delta )
121121
122122 else :
123123 invalid_estimates += 1
124124
125125 # estimatesmartfee should still be valid
126- approx_estimate = node .estimatesmartfee (i + 1 )["feerate" ]
127- answer_found = node .estimatesmartfee (i + 1 )["blocks" ]
128- assert (approx_estimate > 0 )
129- assert (answer_found > i + 1 )
126+ approx_estimate = node .estimatesmartfee (i + 1 )["feerate" ]
127+ answer_found = node .estimatesmartfee (i + 1 )["blocks" ]
128+ assert_greater_than (approx_estimate , 0 )
129+ assert_greater_than (answer_found , i + 1 )
130130
131131 # Once we're at a high enough confirmation count that we can give an estimate
132132 # We should have estimates for all higher confirmation counts
@@ -136,7 +136,7 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True):
136136 # Check on the expected number of different confirmation counts
137137 # that we might not have valid estimates for
138138 if invalid_estimates > max_invalid :
139- raise AssertionError ("More than (%d) invalid estimates" % (max_invalid ))
139+ raise AssertionError ("More than (%d) invalid estimates" % (max_invalid ))
140140 return all_estimates
141141
142142
@@ -160,7 +160,6 @@ def setup_network(self):
160160 # Node2 is a stingy miner, that
161161 # produces too small blocks (room for only 55 or so transactions)
162162
163-
164163 def transact_and_mine (self , numblocks , mining_node ):
165164 min_fee = Decimal ("0.00001" )
166165 # We will now mine numblocks blocks generating on average 100 transactions between each block
@@ -169,14 +168,14 @@ def transact_and_mine(self, numblocks, mining_node):
169168 # resorting to tx's that depend on the mempool when those run out
170169 for i in range (numblocks ):
171170 random .shuffle (self .confutxo )
172- for j in range (random .randrange (100 - 50 ,100 + 50 )):
173- from_index = random .randint (1 ,2 )
171+ for j in range (random .randrange (100 - 50 , 100 + 50 )):
172+ from_index = random .randint (1 , 2 )
174173 (txhex , fee ) = small_txpuzzle_randfee (self .nodes [from_index ], self .confutxo ,
175174 self .memutxo , Decimal ("0.005" ), min_fee , min_fee )
176175 tx_kbytes = (len (txhex ) // 2 ) / 1000.0
177- self .fees_per_kb .append (float (fee )/ tx_kbytes )
176+ self .fees_per_kb .append (float (fee ) / tx_kbytes )
178177 sync_mempools (self .nodes [0 :3 ], wait = .1 )
179- mined = mining_node .getblock (mining_node .generate (1 )[0 ],True )["tx" ]
178+ mined = mining_node .getblock (mining_node .generate (1 )[0 ], True )["tx" ]
180179 sync_blocks (self .nodes [0 :3 ], wait = .1 )
181180 # update which txouts are confirmed
182181 newmem = []
@@ -210,13 +209,13 @@ def run_test(self):
210209 # Use txouts to monitor the available utxo, since these won't be tracked in wallet
211210 reps = 0
212211 while (reps < 5 ):
213- #Double txouts to txouts2
214- while (len (self .txouts )> 0 ):
212+ # Double txouts to txouts2
213+ while (len (self .txouts ) > 0 ):
215214 split_inputs (self .nodes [0 ], self .txouts , self .txouts2 )
216215 while (len (self .nodes [0 ].getrawmempool ()) > 0 ):
217216 self .nodes [0 ].generate (1 )
218- #Double txouts2 to txouts
219- while (len (self .txouts2 )> 0 ):
217+ # Double txouts2 to txouts
218+ while (len (self .txouts2 ) > 0 ):
220219 split_inputs (self .nodes [0 ], self .txouts2 , self .txouts )
221220 while (len (self .nodes [0 ].getrawmempool ()) > 0 ):
222221 self .nodes [0 ].generate (1 )
@@ -235,7 +234,7 @@ def run_test(self):
235234
236235 self .fees_per_kb = []
237236 self .memutxo = []
238- self .confutxo = self .txouts # Start with the set of confirmed txouts after splitting
237+ self .confutxo = self .txouts # Start with the set of confirmed txouts after splitting
239238 self .log .info ("Will output estimates for 1/2/3/6/15/25 blocks" )
240239
241240 for i in range (2 ):
0 commit comments