66
77In this test we connect to one node over p2p, and test tx requests."""
88from test_framework .blocktools import create_block , create_coinbase , create_transaction
9- from test_framework .messages import COIN
10- from test_framework .mininode import network_thread_start , P2PDataStore
9+ from test_framework .messages import (
10+ COIN ,
11+ COutPoint ,
12+ CTransaction ,
13+ CTxIn ,
14+ CTxOut ,
15+ )
16+ from test_framework .mininode import network_thread_start , P2PDataStore , network_thread_join
1117from test_framework .test_framework import BitcoinTestFramework
18+ from test_framework .util import (
19+ assert_equal ,
20+ wait_until ,
21+ )
1222
13- class InvalidTxRequestTest (BitcoinTestFramework ):
1423
24+ class InvalidTxRequestTest (BitcoinTestFramework ):
1525 def set_test_params (self ):
1626 self .num_nodes = 1
1727 self .setup_clean_chain = True
18- self .extra_args = [["-whitelist=127.0.0.1" ]]
28+
29+ def bootstrap_p2p (self , * , num_connections = 1 ):
30+ """Add a P2P connection to the node.
31+
32+ Helper to connect and wait for version handshake."""
33+ for _ in range (num_connections ):
34+ self .nodes [0 ].add_p2p_connection (P2PDataStore ())
35+ network_thread_start ()
36+ self .nodes [0 ].p2p .wait_for_verack ()
37+
38+ def reconnect_p2p (self , ** kwargs ):
39+ """Tear down and bootstrap the P2P connection to the node.
40+
41+ The node gets disconnected several times in this test. This helper
42+ method reconnects the p2p and restarts the network thread."""
43+ self .nodes [0 ].disconnect_p2ps ()
44+ network_thread_join ()
45+ self .bootstrap_p2p (** kwargs )
1946
2047 def run_test (self ):
21- # Add p2p connection to node0
2248 node = self .nodes [0 ] # convenience reference to the node
23- node .add_p2p_connection (P2PDataStore ())
2449
25- network_thread_start ()
26- node .p2p .wait_for_verack ()
50+ self .bootstrap_p2p () # Add one p2p connection to the node
2751
2852 best_block = self .nodes [0 ].getbestblockhash ()
2953 tip = int (best_block , 16 )
@@ -44,12 +68,73 @@ def run_test(self):
4468
4569 # b'\x64' is OP_NOTIF
4670 # Transaction will be rejected with code 16 (REJECT_INVALID)
71+ # and we get disconnected immediately
72+ self .log .info ('Test a transaction that is rejected' )
4773 tx1 = create_transaction (block1 .vtx [0 ], 0 , b'\x64 ' , 50 * COIN - 12000 )
48- node .p2p .send_txs_and_test ([tx1 ], node , success = False , reject_code = 16 , reject_reason = b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)' )
74+ node .p2p .send_txs_and_test ([tx1 ], node , success = False , expect_disconnect = True )
75+
76+ # Make two p2p connections to provide the node with orphans
77+ # * p2ps[0] will send valid orphan txs (one with low fee)
78+ # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
79+ self .reconnect_p2p (num_connections = 2 )
80+
81+ self .log .info ('Test orphan transaction handling ... ' )
82+ # Create a root transaction that we withold until all dependend transactions
83+ # are sent out and in the orphan cache
84+ tx_withhold = CTransaction ()
85+ tx_withhold .vin .append (CTxIn (outpoint = COutPoint (block1 .vtx [0 ].sha256 , 0 )))
86+ tx_withhold .vout .append (CTxOut (nValue = 50 * COIN - 12000 , scriptPubKey = b'\x51 ' ))
87+ tx_withhold .calc_sha256 ()
88+
89+ # Our first orphan tx with some outputs to create further orphan txs
90+ tx_orphan_1 = CTransaction ()
91+ tx_orphan_1 .vin .append (CTxIn (outpoint = COutPoint (tx_withhold .sha256 , 0 )))
92+ tx_orphan_1 .vout = [CTxOut (nValue = 10 * COIN , scriptPubKey = b'\x51 ' )] * 3
93+ tx_orphan_1 .calc_sha256 ()
94+
95+ # A valid transaction with low fee
96+ tx_orphan_2_no_fee = CTransaction ()
97+ tx_orphan_2_no_fee .vin .append (CTxIn (outpoint = COutPoint (tx_orphan_1 .sha256 , 0 )))
98+ tx_orphan_2_no_fee .vout .append (CTxOut (nValue = 10 * COIN , scriptPubKey = b'\x51 ' ))
99+
100+ # A valid transaction with sufficient fee
101+ tx_orphan_2_valid = CTransaction ()
102+ tx_orphan_2_valid .vin .append (CTxIn (outpoint = COutPoint (tx_orphan_1 .sha256 , 1 )))
103+ tx_orphan_2_valid .vout .append (CTxOut (nValue = 10 * COIN - 12000 , scriptPubKey = b'\x51 ' ))
104+ tx_orphan_2_valid .calc_sha256 ()
105+
106+ # An invalid transaction with negative fee
107+ tx_orphan_2_invalid = CTransaction ()
108+ tx_orphan_2_invalid .vin .append (CTxIn (outpoint = COutPoint (tx_orphan_1 .sha256 , 2 )))
109+ tx_orphan_2_invalid .vout .append (CTxOut (nValue = 11 * COIN , scriptPubKey = b'\x51 ' ))
110+
111+ self .log .info ('Send the orphans ... ' )
112+ # Send valid orphan txs from p2ps[0]
113+ node .p2p .send_txs_and_test ([tx_orphan_1 , tx_orphan_2_no_fee , tx_orphan_2_valid ], node , success = False )
114+ # Send invalid tx from p2ps[1]
115+ node .p2ps [1 ].send_txs_and_test ([tx_orphan_2_invalid ], node , success = False )
116+
117+ assert_equal (0 , node .getmempoolinfo ()['size' ]) # Mempool should be empty
118+ assert_equal (2 , len (node .getpeerinfo ())) # p2ps[1] is still connected
119+
120+ self .log .info ('Send the withhold tx ... ' )
121+ node .p2p .send_txs_and_test ([tx_withhold ], node , success = True )
122+
123+ # Transactions that should end up in the mempool
124+ expected_mempool = {
125+ t .hash
126+ for t in [
127+ tx_withhold , # The transaction that is the root for all orphans
128+ tx_orphan_1 , # The orphan transaction that splits the coins
129+ tx_orphan_2_valid , # The valid transaction (with sufficient fee)
130+ ]
131+ }
132+ # Transactions that do not end up in the mempool
133+ # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
134+ # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)
49135
50- # Verify valid transaction
51- tx1 = create_transaction (block1 .vtx [0 ], 0 , b'' , 50 * COIN - 12000 )
52- node .p2p .send_txs_and_test ([tx1 ], node , success = True )
136+ wait_until (lambda : 1 == len (node .getpeerinfo ()), timeout = 12 ) # p2ps[1] is no longer connected
137+ assert_equal (expected_mempool , set (node .getrawmempool ()))
53138
54139
55140if __name__ == '__main__' :
0 commit comments