10 from collections
import OrderedDict
17 P2SH_1 =
"8kctg1WWKdoLveifyNnDYtRAqBPpqgL8z2" 18 P2SH_2 =
"8xp4fcNB8rz9UbZC47tv6eui1ZSPMd3iYT" 21 SCRIPT_SIG = [
"0451025175",
"0451025275"]
25 if isinstance(o, Decimal):
27 return super(DecimalEncoder, self).
default(o)
31 Since dictionaries in python are unsorted make sure that our outputs are correctly ordered. 32 Note: comparing strings to get "correct order" is based on the fact that 33 P2SH_1 string is < P2SH_2 string in this particular case. 35 outputs_unordered = json.dumps(outputs, cls=DecimalEncoder)
36 outputs_ordered = json.dumps(outputs, sort_keys=
True, cls=DecimalEncoder)
37 if outputs_ordered != outputs_unordered:
38 first_rawoutput = rawtx[12+82*inputnum:12+82*inputnum+64]
39 second_rawoutput = rawtx[12+82*inputnum+64:12+82*inputnum+64+64]
40 rawtx = rawtx[0:12+82*inputnum] + second_rawoutput + first_rawoutput + rawtx[12+82*inputnum+64+64:]
45 Create and send a transaction with a random fee. 46 The transaction pays to a trivial P2SH script, and assumes that its inputs 48 The function takes a list of confirmed outputs and unconfirmed outputs 49 and attempts to use the confirmed list first for its inputs. 50 It adds the newly created outputs to the unconfirmed list. 51 Returns (raw transaction, fee) 56 rand_fee = float(fee_increment)*(1.1892**random.randint(0,28))
60 total_in = Decimal(
"0.00000000")
61 while total_in <= (amount + fee)
and len(conflist) > 0:
63 total_in += t[
"amount"]
64 inputs.append({
"txid" : t[
"txid"],
"vout" : t[
"vout"]} )
65 if total_in <= amount + fee:
66 while total_in <= (amount + fee)
and len(unconflist) > 0:
68 total_in += t[
"amount"]
69 inputs.append({
"txid" : t[
"txid"],
"vout" : t[
"vout"]} )
70 if total_in <= amount + fee:
71 raise RuntimeError(
"Insufficient funds: need %d, have %d"%(amount+fee, total_in))
73 outputs = OrderedDict([(P2SH_1, total_in - amount - fee),
75 rawtx = from_node.createrawtransaction(inputs, outputs)
80 completetx = rawtx[0:10]
83 completetx += rawtx[10+82*inputnum:82+82*inputnum]
84 completetx += SCRIPT_SIG[inp[
"vout"]]
85 completetx += rawtx[84+82*inputnum:92+82*inputnum]
87 completetx += rawtx[10+82*inputnum:]
88 txid = from_node.sendrawtransaction(completetx,
True)
89 unconflist.append({
"txid" : txid,
"vout" : 0 ,
"amount" : total_in - amount - fee})
90 unconflist.append({
"txid" : txid,
"vout" : 1 ,
"amount" : amount})
92 return (completetx, fee)
96 We need to generate a lot of very small inputs so we can generate a ton of transactions 97 and they will have low priority. 98 This function takes an input from txins, and creates and sends a transaction 99 which splits the value into 2 outputs which are appended to txouts. 101 prevtxout = txins.pop()
103 inputs.append({
"txid" : prevtxout[
"txid"],
"vout" : prevtxout[
"vout"] })
105 rem_change = prevtxout[
"amount"] - half_change - Decimal(
"0.00010000")
106 outputs = OrderedDict([(P2SH_1, half_change), (P2SH_2, rem_change)])
107 rawtx = from_node.createrawtransaction(inputs, outputs)
112 completetx = from_node.signrawtransaction(rawtx)[
"hex"]
114 completetx = rawtx[0:82] + SCRIPT_SIG[prevtxout[
"vout"]] + rawtx[84:]
115 txid = from_node.sendrawtransaction(completetx,
True)
116 txouts.append({
"txid" : txid,
"vout" : 0 ,
"amount" : half_change})
117 txouts.append({
"txid" : txid,
"vout" : 1 ,
"amount" : rem_change})
121 This function calls estimatefee and verifies that the estimates 122 meet certain invariants. 124 all_estimates = [ node.estimatefee(i)
for i
in range(1,26) ]
126 print([str(all_estimates[e-1])
for e
in [1,2,3,6,15,25]])
128 last_e = max(fees_seen)
129 for e
in [x
for x
in all_estimates
if x >= 0]:
131 if float(e)+delta < min(fees_seen)
or float(e)-delta > max(fees_seen):
132 raise AssertionError(
"Estimated fee (%f) out of range (%f,%f)" 133 %(float(e), min(fees_seen), max(fees_seen)))
135 if float(e)-delta > last_e:
136 raise AssertionError(
"Estimated fee (%f) larger than last fee (%f) for lower number of confirms" 137 %(float(e),float(last_e)))
139 valid_estimate =
False 140 invalid_estimates = 0
141 for i,e
in enumerate(all_estimates):
143 valid_estimate =
True 148 invalid_estimates += 1
151 approx_estimate = node.estimatesmartfee(i+1)[
"feerate"]
152 answer_found = node.estimatesmartfee(i+1)[
"blocks"]
153 assert(approx_estimate > 0)
154 assert(answer_found > i+1)
159 raise AssertionError(
"Invalid estimate appears at higher confirm count than valid estimate")
163 if invalid_estimates > max_invalid:
164 raise AssertionError(
"More than (%d) invalid estimates"%(max_invalid))
172 We'll setup the network to have 3 nodes that all mine with different parameters. 173 But first we need to use one node to create a lot of small low priority outputs 174 which we will use to generate our transactions. 178 self.
nodes.append(
start_node(0, self.options.tmpdir, [
"-maxorphantx=1000",
179 "-relaypriority=0",
"-whitelist=127.0.0.1"]))
181 print(
"This test is time consuming, please be patient")
182 print(
"Splitting inputs to small size so we can generate low priority tx's")
197 while (len(self.
txouts)>0):
207 print(
"Finished splitting")
216 [
"-blockprioritysize=1500",
"-blockmaxsize=17000",
217 "-maxorphantx=1000",
"-relaypriority=0",
"-debug=estimatefee"]))
222 node2args = [
"-blockprioritysize=0",
"-blockmaxsize=8000",
"-maxorphantx=1000",
"-relaypriority=0"]
232 min_fee = Decimal(
"0.0001")
237 for i
in range(numblocks):
239 for j
in range(random.randrange(100-50,100+50)):
240 from_index = random.randint(1,2)
242 self.
memutxo, Decimal(
"0.005"), min_fee, min_fee)
243 tx_kbytes = (len(txhex) // 2) / 1000.0
246 mined = mining_node.getblock(mining_node.generate(1)[0],
True)[
"tx"]
251 if utx[
"txid"]
in mined:
261 print(
"Will output estimates for 1/2/3/6/15/25 blocks")
264 print(
"Creating transactions and mining them with a block size that can't keep up")
269 print(
"Creating transactions and mining them at a block size that is just big enough")
280 print(
"Final estimates after emptying mempools")
283 if __name__ ==
'__main__':
def split_inputs(from_node, txins, txouts, initial_split=False)
UniValue listunspent(const UniValue ¶ms, bool fHelp)
def swap_outputs_in_rawtx(rawtx, outputs, inputnum)
def connect_nodes(from_connection, node_num)
def sync_mempools(rpc_connections, wait=1)
def transact_and_mine(self, numblocks, mining_node)
def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment)
def check_estimates(node, fees_seen, max_invalid, print_estimates=True)
def satoshi_round(amount)
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
UniValue generate(const UniValue ¶ms, bool fHelp)
def sync_blocks(rpc_connections, wait=1)
UniValue getrawmempool(const UniValue ¶ms, bool fHelp)
def assert_equal(thing1, thing2)