@@ -15,22 +15,24 @@ class MempoolPackagesTest(BitcoinTestFramework):
1515
1616 def setup_network (self ):
1717 self .nodes = []
18- self .nodes .append (start_node (0 , self .options .tmpdir , ["-maxorphantx=1000" , "-relaypriority=0" ]))
18+ self .nodes .append (start_node (0 , self .options .tmpdir , ["-maxorphantx=1000" , "-relaypriority=0" , "-debug" ]))
19+ self .nodes .append (start_node (1 , self .options .tmpdir , ["-maxorphantx=1000" , "-relaypriority=0" , "-limitancestorcount=5" , "-debug" ]))
20+ connect_nodes (self .nodes [0 ], 1 )
1921 self .is_network_split = False
2022 self .sync_all ()
2123
2224 # Build a transaction that spends parent_txid:vout
2325 # Return amount sent
24- def chain_transaction (self , parent_txid , vout , value , fee , num_outputs ):
26+ def chain_transaction (self , node , parent_txid , vout , value , fee , num_outputs ):
2527 send_value = satoshi_round ((value - fee )/ num_outputs )
2628 inputs = [ {'txid' : parent_txid , 'vout' : vout } ]
2729 outputs = {}
2830 for i in xrange (num_outputs ):
29- outputs [self . nodes [ 0 ] .getnewaddress ()] = send_value
30- rawtx = self . nodes [ 0 ] .createrawtransaction (inputs , outputs )
31- signedtx = self . nodes [ 0 ] .signrawtransaction (rawtx )
32- txid = self . nodes [ 0 ] .sendrawtransaction (signedtx ['hex' ])
33- fulltx = self . nodes [ 0 ] .getrawtransaction (txid , 1 )
31+ outputs [node .getnewaddress ()] = send_value
32+ rawtx = node .createrawtransaction (inputs , outputs )
33+ signedtx = node .signrawtransaction (rawtx )
34+ txid = node .sendrawtransaction (signedtx ['hex' ])
35+ fulltx = node .getrawtransaction (txid , 1 )
3436 assert (len (fulltx ['vout' ]) == num_outputs ) # make sure we didn't generate a change output
3537 return (txid , send_value )
3638
@@ -46,7 +48,7 @@ def run_test(self):
4648 # 100 transactions off a confirmed tx should be fine
4749 chain = []
4850 for i in xrange (100 ):
49- (txid , sent_value ) = self .chain_transaction (txid , 0 , value , fee , 1 )
51+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], txid , 0 , value , fee , 1 )
5052 value = sent_value
5153 chain .append (txid )
5254
@@ -69,10 +71,12 @@ def run_test(self):
6971
7072 # Adding one more transaction on to the chain should fail.
7173 try :
72- self .chain_transaction (txid , vout , value , fee , 1 )
74+ self .chain_transaction (self . nodes [ 0 ], txid , vout , value , fee , 1 )
7375 except JSONRPCException as e :
7476 print "too-long-ancestor-chain successfully rejected"
7577
78+ # TODO: check that node1's mempool is as expected
79+
7680 # TODO: test ancestor size limits
7781
7882 # Now test descendant chain limits
@@ -82,15 +86,15 @@ def run_test(self):
8286
8387 transaction_package = []
8488 # First create one parent tx with 10 children
85- (txid , sent_value ) = self .chain_transaction (txid , vout , value , fee , 10 )
89+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], txid , vout , value , fee , 10 )
8690 parent_transaction = txid
8791 for i in xrange (10 ):
8892 transaction_package .append ({'txid' : txid , 'vout' : i , 'amount' : sent_value })
8993
9094 for i in xrange (1000 ):
9195 utxo = transaction_package .pop (0 )
9296 try :
93- (txid , sent_value ) = self .chain_transaction (utxo ['txid' ], utxo ['vout' ], utxo ['amount' ], fee , 10 )
97+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], utxo ['txid' ], utxo ['vout' ], utxo ['amount' ], fee , 10 )
9498 for j in xrange (10 ):
9599 transaction_package .append ({'txid' : txid , 'vout' : j , 'amount' : sent_value })
96100 if i == 998 :
@@ -101,7 +105,74 @@ def run_test(self):
101105 assert_equal (i , 999 )
102106 print "tx that would create too large descendant package successfully rejected"
103107
108+ # TODO: check that node1's mempool is as expected
109+
104110 # TODO: test descendant size limits
105111
112+ # Test reorg handling
113+ # First, the basics:
114+ self .nodes [0 ].generate (1 )
115+ sync_blocks (self .nodes )
116+ self .nodes [1 ].invalidateblock (self .nodes [0 ].getbestblockhash ())
117+ self .nodes [1 ].reconsiderblock (self .nodes [0 ].getbestblockhash ())
118+
119+ # Now test the case where node1 has a transaction T in its mempool that
120+ # depends on transactions A and B which are in a mined block, and the
121+ # block containing A and B is disconnected, AND B is not accepted back
122+ # into node1's mempool because its ancestor count is too high.
123+
124+ # Create 8 transactions, like so:
125+ # Tx0 -> Tx1 (vout0)
126+ # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
127+ #
128+ # Mine them in the next block, then generate a new tx8 that spends
129+ # Tx1 and Tx7, and add to node1's mempool, then disconnect the
130+ # last block.
131+
132+ # Create tx0 with 2 outputs
133+ utxo = self .nodes [0 ].listunspent ()
134+ txid = utxo [0 ]['txid' ]
135+ value = utxo [0 ]['amount' ]
136+ vout = utxo [0 ]['vout' ]
137+
138+ send_value = satoshi_round ((value - fee )/ 2 )
139+ inputs = [ {'txid' : txid , 'vout' : vout } ]
140+ outputs = {}
141+ for i in xrange (2 ):
142+ outputs [self .nodes [0 ].getnewaddress ()] = send_value
143+ rawtx = self .nodes [0 ].createrawtransaction (inputs , outputs )
144+ signedtx = self .nodes [0 ].signrawtransaction (rawtx )
145+ txid = self .nodes [0 ].sendrawtransaction (signedtx ['hex' ])
146+ tx0_id = txid
147+ value = send_value
148+
149+ # Create tx1
150+ (tx1_id , tx1_value ) = self .chain_transaction (self .nodes [0 ], tx0_id , 0 , value , fee , 1 )
151+
152+ # Create tx2-7
153+ vout = 1
154+ txid = tx0_id
155+ for i in xrange (6 ):
156+ (txid , sent_value ) = self .chain_transaction (self .nodes [0 ], txid , vout , value , fee , 1 )
157+ vout = 0
158+ value = sent_value
159+
160+ # Mine these in a block
161+ self .nodes [0 ].generate (1 )
162+ self .sync_all ()
163+
164+ # Now generate tx8, with a big fee
165+ inputs = [ {'txid' : tx1_id , 'vout' : 0 }, {'txid' : txid , 'vout' : 0 } ]
166+ outputs = { self .nodes [0 ].getnewaddress () : send_value + value - 4 * fee }
167+ rawtx = self .nodes [0 ].createrawtransaction (inputs , outputs )
168+ signedtx = self .nodes [0 ].signrawtransaction (rawtx )
169+ txid = self .nodes [0 ].sendrawtransaction (signedtx ['hex' ])
170+ sync_mempools (self .nodes )
171+
172+ # Now try to disconnect the tip on each node...
173+ self .nodes [1 ].invalidateblock (self .nodes [1 ].getbestblockhash ())
174+ self .nodes [0 ].invalidateblock (self .nodes [0 ].getbestblockhash ())
175+ sync_blocks (self .nodes )
176+
106177if __name__ == '__main__' :
107178 MempoolPackagesTest ().main ()
0 commit comments