6
6
7
7
In this test we connect to one node over p2p, and test tx requests."""
8
8
from 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
11
17
from test_framework .test_framework import BitcoinTestFramework
18
+ from test_framework .util import (
19
+ assert_equal ,
20
+ wait_until ,
21
+ )
12
22
13
- class InvalidTxRequestTest (BitcoinTestFramework ):
14
23
24
+ class InvalidTxRequestTest (BitcoinTestFramework ):
15
25
def set_test_params (self ):
16
26
self .num_nodes = 1
17
27
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 )
19
46
20
47
def run_test (self ):
21
- # Add p2p connection to node0
22
48
node = self .nodes [0 ] # convenience reference to the node
23
- node .add_p2p_connection (P2PDataStore ())
24
49
25
- network_thread_start ()
26
- node .p2p .wait_for_verack ()
50
+ self .bootstrap_p2p () # Add one p2p connection to the node
27
51
28
52
best_block = self .nodes [0 ].getbestblockhash ()
29
53
tip = int (best_block , 16 )
@@ -44,12 +68,73 @@ def run_test(self):
44
68
45
69
# b'\x64' is OP_NOTIF
46
70
# 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' )
47
73
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)
49
135
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 ()))
53
138
54
139
55
140
if __name__ == '__main__' :
0 commit comments