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
14 class MempoolPackagesTest(BitcoinTestFramework
):
15 def set_test_params(self
):
17 self
.extra_args
= [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]]
19 # Build a transaction that spends parent_txid:vout
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
} ]
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
)
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
45 for i
in range(MAX_ANCESTORS
):
46 (txid
, sent_value
) = self
.chain_transaction(self
.nodes
[0], txid
, 0, value
, fee
, 1)
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
)
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
)
74 # Check that getmempooldescendants is correct
75 assert_equal(sorted(descendants
), sorted(self
.nodes
[0].getmempooldescendants(x
)))
78 # Check that getmempoolancestors is correct
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)
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)
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_jsonrpc(-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)
136 for x
in reversed(chain
):
137 descendant_fees
+= mempool
[x
]['fee']
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
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)
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_jsonrpc(-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
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:
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
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
} ]
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'])
214 tx1_id
, _
= self
.chain_transaction(self
.nodes
[0], tx0_id
, 0, value
, fee
, 1)
220 (txid
, sent_value
) = self
.chain_transaction(self
.nodes
[0], txid
, vout
, value
, fee
, 1)
224 # Mine these in a block
225 self
.nodes
[0].generate(1)
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()