1212 Decimal ,
1313 ROUND_DOWN ,
1414 JSONRPCException ,
15+ sync_blocks ,
16+ sync_mempools
1517)
1618
1719def satoshi_round (amount ):
@@ -22,21 +24,21 @@ def satoshi_round(amount):
2224
2325class MempoolPackagesTest (PivxTestFramework ):
2426 def set_test_params (self ):
25- self .num_nodes = 1
26- self .extra_args = [["-maxorphantx=1000" , "-relaypriority=0" ]]
27+ self .num_nodes = 2
28+ self .extra_args = [["-maxorphantx=1000" , "-relaypriority=0" ], [ "-maxorphantx=1000" , "-relaypriority=0" , "-limitancestorcount=5" ] ]
2729
2830 # Build a transaction that spends parent_txid:vout
2931 # Return amount sent
30- def chain_transaction (self , parent_txid , vout , value , fee , num_outputs ):
32+ def chain_transaction (self , node , parent_txid , vout , value , fee , num_outputs ):
3133 send_value = satoshi_round ((value - fee )/ num_outputs )
3234 inputs = [{'txid' : parent_txid , 'vout' : vout }]
3335 outputs = {}
3436 for i in range (num_outputs ):
35- outputs [self . nodes [ 0 ] .getnewaddress ()] = float (send_value )
36- rawtx = self . nodes [ 0 ] .createrawtransaction (inputs , outputs )
37- signedtx = self . nodes [ 0 ] .signrawtransaction (rawtx )
38- txid = self . nodes [ 0 ] .sendrawtransaction (signedtx ['hex' ])
39- fulltx = self . nodes [ 0 ] .getrawtransaction (txid , 1 )
37+ outputs [node .getnewaddress ()] = float (send_value )
38+ rawtx = node .createrawtransaction (inputs , outputs )
39+ signedtx = node .signrawtransaction (rawtx )
40+ txid = node .sendrawtransaction (signedtx ['hex' ])
41+ fulltx = node .getrawtransaction (txid , 1 )
4042 assert (len (fulltx ['vout' ]) == num_outputs ) # make sure we didn't generate a change output
4143 return (txid , send_value )
4244
@@ -51,7 +53,7 @@ def run_test(self):
5153 # MAX_ANCESTORS transactions off a confirmed tx should be fine
5254 chain = []
5355 for i in range (MAX_ANCESTORS ):
54- (txid , sent_value ) = self .chain_transaction (txid , 0 , value , fee , 1 )
56+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], txid , 0 , value , fee , 1 )
5557 value = sent_value
5658 chain .append (txid )
5759
@@ -74,10 +76,12 @@ def run_test(self):
7476
7577 # Adding one more transaction on to the chain should fail.
7678 try :
77- self .chain_transaction (txid , vout , value , fee , 1 )
79+ self .chain_transaction (self . nodes [ 0 ], txid , vout , value , fee , 1 )
7880 except JSONRPCException as e :
7981 self .log .info ("too-long-ancestor-chain successfully rejected" )
8082
83+ # TODO: check that node1's mempool is as expected
84+
8185 # TODO: test ancestor size limits
8286
8387 # Now test descendant chain limits
@@ -87,15 +91,15 @@ def run_test(self):
8791
8892 transaction_package = []
8993 # First create one parent tx with 10 children
90- (txid , sent_value ) = self .chain_transaction (txid , vout , value , fee , 10 )
94+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], txid , vout , value , fee , 10 )
9195 parent_transaction = txid
9296 for i in range (10 ):
9397 transaction_package .append ({'txid' : txid , 'vout' : i , 'amount' : sent_value })
9498
9599 for i in range (MAX_DESCENDANTS ):
96100 utxo = transaction_package .pop (0 )
97101 try :
98- (txid , sent_value ) = self .chain_transaction (utxo ['txid' ], utxo ['vout' ], utxo ['amount' ], fee , 10 )
102+ (txid , sent_value ) = self .chain_transaction (self . nodes [ 0 ], utxo ['txid' ], utxo ['vout' ], utxo ['amount' ], fee , 10 )
99103 for j in range (10 ):
100104 transaction_package .append ({'txid' : txid , 'vout' : j , 'amount' : sent_value })
101105 if i == MAX_DESCENDANTS - 2 :
@@ -106,7 +110,74 @@ def run_test(self):
106110 assert_equal (i , MAX_DESCENDANTS - 1 )
107111 self .log .info ("tx that would create too large descendant package successfully rejected" )
108112
113+ # TODO: check that node1's mempool is as expected
114+
109115 # TODO: test descendant size limits
110116
117+ # Test reorg handling
118+ # First, the basics:
119+ self .nodes [0 ].generate (1 )
120+ sync_blocks (self .nodes )
121+ self .nodes [1 ].invalidateblock (self .nodes [0 ].getbestblockhash ())
122+ self .nodes [1 ].reconsiderblock (self .nodes [0 ].getbestblockhash ())
123+
124+ # Now test the case where node1 has a transaction T in its mempool that
125+ # depends on transactions A and B which are in a mined block, and the
126+ # block containing A and B is disconnected, AND B is not accepted back
127+ # into node1's mempool because its ancestor count is too high.
128+
129+ # Create 8 transactions, like so:
130+ # Tx0 -> Tx1 (vout0)
131+ # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
132+ #
133+ # Mine them in the next block, then generate a new tx8 that spends
134+ # Tx1 and Tx7, and add to node1's mempool, then disconnect the
135+ # last block.
136+
137+ # Create tx0 with 2 outputs
138+ utxo = self .nodes [0 ].listunspent ()
139+ txid = utxo [0 ]['txid' ]
140+ value = utxo [0 ]['amount' ]
141+ vout = utxo [0 ]['vout' ]
142+
143+ send_value = satoshi_round ((value - fee )/ 2 )
144+ inputs = [ {'txid' : txid , 'vout' : vout } ]
145+ outputs = {}
146+ for i in range (2 ):
147+ outputs [self .nodes [0 ].getnewaddress ()] = float (send_value )
148+ rawtx = self .nodes [0 ].createrawtransaction (inputs , outputs )
149+ signedtx = self .nodes [0 ].signrawtransaction (rawtx )
150+ txid = self .nodes [0 ].sendrawtransaction (signedtx ['hex' ])
151+ tx0_id = txid
152+ value = send_value
153+
154+ # Create tx1
155+ (tx1_id , tx1_value ) = self .chain_transaction (self .nodes [0 ], tx0_id , 0 , value , fee , 1 )
156+
157+ # Create tx2-7
158+ vout = 1
159+ txid = tx0_id
160+ for i in range (6 ):
161+ (txid , sent_value ) = self .chain_transaction (self .nodes [0 ], txid , vout , value , fee , 1 )
162+ vout = 0
163+ value = sent_value
164+
165+ # Mine these in a block
166+ self .nodes [0 ].generate (1 )
167+ self .sync_all ()
168+
169+ # Now generate tx8, with a big fee
170+ inputs = [ {'txid' : tx1_id , 'vout' : 0 }, {'txid' : txid , 'vout' : 0 } ]
171+ outputs = { self .nodes [0 ].getnewaddress () : send_value + value - 4 * fee }
172+ rawtx = self .nodes [0 ].createrawtransaction (inputs , outputs )
173+ signedtx = self .nodes [0 ].signrawtransaction (rawtx )
174+ txid = self .nodes [0 ].sendrawtransaction (signedtx ['hex' ])
175+ sync_mempools (self .nodes )
176+
177+ # Now try to disconnect the tip on each node...
178+ self .nodes [1 ].invalidateblock (self .nodes [1 ].getbestblockhash ())
179+ self .nodes [0 ].invalidateblock (self .nodes [0 ].getbestblockhash ())
180+ sync_blocks (self .nodes )
181+
111182if __name__ == '__main__' :
112183 MempoolPackagesTest ().main ()
0 commit comments