Dash Core  0.12.2.1
P2P Digital Currency
mempool_packages.py
Go to the documentation of this file.
1 #!/usr/bin/env python2
2 # Copyright (c) 2014-2015 The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 # Test descendant package tracking code
7 
8 from test_framework.test_framework import BitcoinTestFramework
9 from test_framework.util import *
10 from test_framework.mininode import COIN
11 
12 MAX_ANCESTORS = 25
13 MAX_DESCENDANTS = 25
14 
16 
17  def setup_network(self):
18  self.nodes = []
19  self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-debug"]))
20  self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-limitancestorcount=5", "-debug"]))
21  connect_nodes(self.nodes[0], 1)
22  self.is_network_split = False
23  self.sync_all()
24 
25  # Build a transaction that spends parent_txid:vout
26  # Return amount sent
27  def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
28  send_value = satoshi_round((value - fee)/num_outputs)
29  inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
30  outputs = {}
31  for i in xrange(num_outputs):
32  outputs[node.getnewaddress()] = send_value
33  rawtx = node.createrawtransaction(inputs, outputs)
34  signedtx = node.signrawtransaction(rawtx)
35  txid = node.sendrawtransaction(signedtx['hex'])
36  fulltx = node.getrawtransaction(txid, 1)
37  assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output
38  return (txid, send_value)
39 
40  def run_test(self):
41  ''' Mine some blocks and have them mature. '''
42  self.nodes[0].generate(101)
43  utxo = self.nodes[0].listunspent(10)
44  txid = utxo[0]['txid']
45  vout = utxo[0]['vout']
46  value = utxo[0]['amount']
47 
48  fee = Decimal("0.0001")
49  # MAX_ANCESTORS transactions off a confirmed tx should be fine
50  chain = []
51  for i in xrange(MAX_ANCESTORS):
52  (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
53  value = sent_value
54  chain.append(txid)
55 
56  # Check mempool has MAX_ANCESTORS transactions in it, and descendant
57  # count and fees should look correct
58  mempool = self.nodes[0].getrawmempool(True)
59  assert_equal(len(mempool), MAX_ANCESTORS)
60  descendant_count = 1
61  descendant_fees = 0
62  descendant_size = 0
63 
64  for x in reversed(chain):
65  assert_equal(mempool[x]['descendantcount'], descendant_count)
66  descendant_fees += mempool[x]['fee']
67  assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
68  assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
69  descendant_size += mempool[x]['size']
70  assert_equal(mempool[x]['descendantsize'], descendant_size)
71  descendant_count += 1
72 
73  # Check that descendant modified fees includes fee deltas from
74  # prioritisetransaction
75  self.nodes[0].prioritisetransaction(chain[-1], 0, 1000)
76  mempool = self.nodes[0].getrawmempool(True)
77 
78  descendant_fees = 0
79  for x in reversed(chain):
80  descendant_fees += mempool[x]['fee']
81  assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
82 
83  # Adding one more transaction on to the chain should fail.
84  try:
85  self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
86  except JSONRPCException as e:
87  print "too-long-ancestor-chain successfully rejected"
88 
89  # Check that prioritising a tx before it's added to the mempool works
90  # First clear the mempool by mining a block.
91  self.nodes[0].generate(1)
92  sync_blocks(self.nodes)
93  assert_equal(len(self.nodes[0].getrawmempool()), 0)
94  # Prioritise a transaction that has been mined, then add it back to the
95  # mempool by using invalidateblock.
96  self.nodes[0].prioritisetransaction(chain[-1], 0, 2000)
97  self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
98  # Keep node1's tip synced with node0
99  self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
100 
101  # Now check that the transaction is in the mempool, with the right modified fee
102  mempool = self.nodes[0].getrawmempool(True)
103 
104  descendant_fees = 0
105  for x in reversed(chain):
106  descendant_fees += mempool[x]['fee']
107  if (x == chain[-1]):
108  assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
109  assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000)
110 
111  # TODO: check that node1's mempool is as expected
112 
113  # TODO: test ancestor size limits
114 
115  # Now test descendant chain limits
116  txid = utxo[1]['txid']
117  value = utxo[1]['amount']
118  vout = utxo[1]['vout']
119 
120  transaction_package = []
121  # First create one parent tx with 10 children
122  (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
123  parent_transaction = txid
124  for i in xrange(10):
125  transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
126 
127  for i in xrange(MAX_DESCENDANTS):
128  utxo = transaction_package.pop(0)
129  try:
130  (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
131  for j in xrange(10):
132  transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})
133  if i == MAX_DESCENDANTS - 2:
134  mempool = self.nodes[0].getrawmempool(True)
135  assert_equal(mempool[parent_transaction]['descendantcount'], MAX_DESCENDANTS)
136  except JSONRPCException as e:
137  print e.error['message']
138  assert_equal(i, MAX_DESCENDANTS - 1)
139  print "tx that would create too large descendant package successfully rejected"
140 
141  # TODO: check that node1's mempool is as expected
142 
143  # TODO: test descendant size limits
144 
145  # Test reorg handling
146  # First, the basics:
147  self.nodes[0].generate(1)
148  sync_blocks(self.nodes)
149  self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
150  self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())
151 
152  # Now test the case where node1 has a transaction T in its mempool that
153  # depends on transactions A and B which are in a mined block, and the
154  # block containing A and B is disconnected, AND B is not accepted back
155  # into node1's mempool because its ancestor count is too high.
156 
157  # Create 8 transactions, like so:
158  # Tx0 -> Tx1 (vout0)
159  # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
160  #
161  # Mine them in the next block, then generate a new tx8 that spends
162  # Tx1 and Tx7, and add to node1's mempool, then disconnect the
163  # last block.
164 
165  # Create tx0 with 2 outputs
166  utxo = self.nodes[0].listunspent()
167  txid = utxo[0]['txid']
168  value = utxo[0]['amount']
169  vout = utxo[0]['vout']
170 
171  send_value = satoshi_round((value - fee)/2)
172  inputs = [ {'txid' : txid, 'vout' : vout} ]
173  outputs = {}
174  for i in xrange(2):
175  outputs[self.nodes[0].getnewaddress()] = send_value
176  rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
177  signedtx = self.nodes[0].signrawtransaction(rawtx)
178  txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
179  tx0_id = txid
180  value = send_value
181 
182  # Create tx1
183  (tx1_id, tx1_value) = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
184 
185  # Create tx2-7
186  vout = 1
187  txid = tx0_id
188  for i in xrange(6):
189  (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
190  vout = 0
191  value = sent_value
192 
193  # Mine these in a block
194  self.nodes[0].generate(1)
195  self.sync_all()
196 
197  # Now generate tx8, with a big fee
198  inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ]
199  outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee }
200  rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
201  signedtx = self.nodes[0].signrawtransaction(rawtx)
202  txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
203  sync_mempools(self.nodes)
204 
205  # Now try to disconnect the tip on each node...
206  self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
207  self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
208  sync_blocks(self.nodes)
209 
210 if __name__ == '__main__':
UniValue prioritisetransaction(const UniValue &params, bool fHelp)
Definition: mining.cpp:279
UniValue reconsiderblock(const UniValue &params, bool fHelp)
UniValue listunspent(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:2533
def connect_nodes(from_connection, node_num)
Definition: util.py:343
UniValue getnewaddress(const UniValue &params, bool fHelp)
Definition: rpcwallet.cpp:113
def sync_mempools(rpc_connections, wait=1)
Definition: util.py:127
UniValue signrawtransaction(const UniValue &params, bool fHelp)
UniValue createrawtransaction(const UniValue &params, bool fHelp)
def satoshi_round(amount)
Definition: util.py:525
def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None)
Definition: util.py:281
UniValue generate(const UniValue &params, bool fHelp)
Definition: mining.cpp:122
UniValue sendrawtransaction(const UniValue &params, bool fHelp)
def sync_blocks(rpc_connections, wait=1)
Definition: util.py:117
UniValue getrawmempool(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:234
UniValue getbestblockhash(const UniValue &params, bool fHelp)
Definition: blockchain.cpp:148
def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs)
def assert_equal(thing1, thing2)
Definition: util.py:461