[tests] Add -blocknotify functional test
[bitcoinplatinum.git] / test / functional / mempool_packages.py
blobb845c756818e4216b22f30bd4c378a29ca528169
1 #!/usr/bin/env python3
2 # Copyright (c) 2014-2016 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 """Test descendant package tracking code."""
7 from test_framework.test_framework import BitcoinTestFramework
8 from test_framework.util import *
9 from test_framework.mininode import COIN
11 MAX_ANCESTORS = 25
12 MAX_DESCENDANTS = 25
14 class MempoolPackagesTest(BitcoinTestFramework):
15 def set_test_params(self):
16 self.num_nodes = 2
17 self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]]
19 # Build a transaction that spends parent_txid:vout
20 # Return amount sent
21 def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
22 send_value = satoshi_round((value - fee)/num_outputs)
23 inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
24 outputs = {}
25 for i in range(num_outputs):
26 outputs[node.getnewaddress()] = send_value
27 rawtx = node.createrawtransaction(inputs, outputs)
28 signedtx = node.signrawtransaction(rawtx)
29 txid = node.sendrawtransaction(signedtx['hex'])
30 fulltx = node.getrawtransaction(txid, 1)
31 assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output
32 return (txid, send_value)
34 def run_test(self):
35 ''' Mine some blocks and have them mature. '''
36 self.nodes[0].generate(101)
37 utxo = self.nodes[0].listunspent(10)
38 txid = utxo[0]['txid']
39 vout = utxo[0]['vout']
40 value = utxo[0]['amount']
42 fee = Decimal("0.0001")
43 # MAX_ANCESTORS transactions off a confirmed tx should be fine
44 chain = []
45 for i in range(MAX_ANCESTORS):
46 (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
47 value = sent_value
48 chain.append(txid)
50 # Check mempool has MAX_ANCESTORS transactions in it, and descendant
51 # count and fees should look correct
52 mempool = self.nodes[0].getrawmempool(True)
53 assert_equal(len(mempool), MAX_ANCESTORS)
54 descendant_count = 1
55 descendant_fees = 0
56 descendant_size = 0
58 descendants = []
59 ancestors = list(chain)
60 for x in reversed(chain):
61 # Check that getmempoolentry is consistent with getrawmempool
62 entry = self.nodes[0].getmempoolentry(x)
63 assert_equal(entry, mempool[x])
65 # Check that the descendant calculations are correct
66 assert_equal(mempool[x]['descendantcount'], descendant_count)
67 descendant_fees += mempool[x]['fee']
68 assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
69 assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN)
70 descendant_size += mempool[x]['size']
71 assert_equal(mempool[x]['descendantsize'], descendant_size)
72 descendant_count += 1
74 # Check that getmempooldescendants is correct
75 assert_equal(sorted(descendants), sorted(self.nodes[0].getmempooldescendants(x)))
76 descendants.append(x)
78 # Check that getmempoolancestors is correct
79 ancestors.remove(x)
80 assert_equal(sorted(ancestors), sorted(self.nodes[0].getmempoolancestors(x)))
82 # Check that getmempoolancestors/getmempooldescendants correctly handle verbose=true
83 v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True)
84 assert_equal(len(v_ancestors), len(chain)-1)
85 for x in v_ancestors.keys():
86 assert_equal(mempool[x], v_ancestors[x])
87 assert(chain[-1] not in v_ancestors.keys())
89 v_descendants = self.nodes[0].getmempooldescendants(chain[0], True)
90 assert_equal(len(v_descendants), len(chain)-1)
91 for x in v_descendants.keys():
92 assert_equal(mempool[x], v_descendants[x])
93 assert(chain[0] not in v_descendants.keys())
95 # Check that ancestor modified fees includes fee deltas from
96 # prioritisetransaction
97 self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
98 mempool = self.nodes[0].getrawmempool(True)
99 ancestor_fees = 0
100 for x in chain:
101 ancestor_fees += mempool[x]['fee']
102 assert_equal(mempool[x]['ancestorfees'], ancestor_fees * COIN + 1000)
104 # Undo the prioritisetransaction for later tests
105 self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)
107 # Check that descendant modified fees includes fee deltas from
108 # prioritisetransaction
109 self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
110 mempool = self.nodes[0].getrawmempool(True)
112 descendant_fees = 0
113 for x in reversed(chain):
114 descendant_fees += mempool[x]['fee']
115 assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000)
117 # Adding one more transaction on to the chain should fail.
118 assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1)
120 # Check that prioritising a tx before it's added to the mempool works
121 # First clear the mempool by mining a block.
122 self.nodes[0].generate(1)
123 sync_blocks(self.nodes)
124 assert_equal(len(self.nodes[0].getrawmempool()), 0)
125 # Prioritise a transaction that has been mined, then add it back to the
126 # mempool by using invalidateblock.
127 self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000)
128 self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
129 # Keep node1's tip synced with node0
130 self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
132 # Now check that the transaction is in the mempool, with the right modified fee
133 mempool = self.nodes[0].getrawmempool(True)
135 descendant_fees = 0
136 for x in reversed(chain):
137 descendant_fees += mempool[x]['fee']
138 if (x == chain[-1]):
139 assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
140 assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 2000)
142 # TODO: check that node1's mempool is as expected
144 # TODO: test ancestor size limits
146 # Now test descendant chain limits
147 txid = utxo[1]['txid']
148 value = utxo[1]['amount']
149 vout = utxo[1]['vout']
151 transaction_package = []
152 # First create one parent tx with 10 children
153 (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
154 parent_transaction = txid
155 for i in range(10):
156 transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
158 # Sign and send up to MAX_DESCENDANT transactions chained off the parent tx
159 for i in range(MAX_DESCENDANTS - 1):
160 utxo = transaction_package.pop(0)
161 (txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
162 for j in range(10):
163 transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})
165 mempool = self.nodes[0].getrawmempool(True)
166 assert_equal(mempool[parent_transaction]['descendantcount'], MAX_DESCENDANTS)
168 # Sending one more chained transaction will fail
169 utxo = transaction_package.pop(0)
170 assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
172 # TODO: check that node1's mempool is as expected
174 # TODO: test descendant size limits
176 # Test reorg handling
177 # First, the basics:
178 self.nodes[0].generate(1)
179 sync_blocks(self.nodes)
180 self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
181 self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())
183 # Now test the case where node1 has a transaction T in its mempool that
184 # depends on transactions A and B which are in a mined block, and the
185 # block containing A and B is disconnected, AND B is not accepted back
186 # into node1's mempool because its ancestor count is too high.
188 # Create 8 transactions, like so:
189 # Tx0 -> Tx1 (vout0)
190 # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
192 # Mine them in the next block, then generate a new tx8 that spends
193 # Tx1 and Tx7, and add to node1's mempool, then disconnect the
194 # last block.
196 # Create tx0 with 2 outputs
197 utxo = self.nodes[0].listunspent()
198 txid = utxo[0]['txid']
199 value = utxo[0]['amount']
200 vout = utxo[0]['vout']
202 send_value = satoshi_round((value - fee)/2)
203 inputs = [ {'txid' : txid, 'vout' : vout} ]
204 outputs = {}
205 for i in range(2):
206 outputs[self.nodes[0].getnewaddress()] = send_value
207 rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
208 signedtx = self.nodes[0].signrawtransaction(rawtx)
209 txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
210 tx0_id = txid
211 value = send_value
213 # Create tx1
214 tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
216 # Create tx2-7
217 vout = 1
218 txid = tx0_id
219 for i in range(6):
220 (txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
221 vout = 0
222 value = sent_value
224 # Mine these in a block
225 self.nodes[0].generate(1)
226 self.sync_all()
228 # Now generate tx8, with a big fee
229 inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ]
230 outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee }
231 rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
232 signedtx = self.nodes[0].signrawtransaction(rawtx)
233 txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
234 sync_mempools(self.nodes)
236 # Now try to disconnect the tip on each node...
237 self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
238 self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
239 sync_blocks(self.nodes)
241 if __name__ == '__main__':
242 MempoolPackagesTest().main()