Skip to content

Commit 195e07f

Browse files
author
MacroFake
committed
Merge bitcoin#19393: test: Add more tests for orphan tx handling
c0a5fce test: Add test for erase orphan tx conflicted by block (Hennadii Stepanov) fa45bb2 test: Add test for erase orphan tx included by block (Hennadii Stepanov) 5c04978 test: Add test for erase orphan tx from peer (Hennadii Stepanov) Pull request description: This PR adds test coverage for the following cases: - erase orphan transactions when a peer is disconnected - erase an orphan transaction when it is included in a new tip block - erase an orphan transaction when it is conflicted with other transactions included in a new tip block Found useful while working on bitcoin#19374. ACKs for top commit: aureleoules: tACK c0a5fce (`make check` and `test/functional/test_runner.py`). kouloumos: ACK c0a5fce with a nit per bitcoin#19393 (comment). pg156: Reviewed to bitcoin@c0a5fce. Concept ACK. Agree due to the lack of RPC calls to inspect orphan pool, using `assert_debug_log` to match strings in log is a reasonable way to test. Tree-SHA512: 98f8deeee2d1c588c7e28a82e513d4a18655084198369db33fe2710458251eeaffed030626940072d7576f57fcbf7d856d761990129e2ca9e372d2ccbd86d07d
2 parents 9fb2a2b + c0a5fce commit 195e07f

File tree

1 file changed

+63
-5
lines changed

1 file changed

+63
-5
lines changed

test/functional/p2p_invalid_tx.py

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ def run_test(self):
6060
block.solve()
6161
# Save the coinbase for later
6262
block1 = block
63-
tip = block.sha256
6463
node.p2ps[0].send_blocks_and_test([block], node, success=True)
6564

6665
self.log.info("Mature the block.")
@@ -93,24 +92,24 @@ def run_test(self):
9392
SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
9493
tx_withhold = CTransaction()
9594
tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
96-
tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
95+
tx_withhold.vout = [CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
9796
tx_withhold.calc_sha256()
9897

9998
# Our first orphan tx with some outputs to create further orphan txs
10099
tx_orphan_1 = CTransaction()
101100
tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
102-
tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
101+
tx_orphan_1.vout = [CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
103102
tx_orphan_1.calc_sha256()
104103

105104
# A valid transaction with low fee
106105
tx_orphan_2_no_fee = CTransaction()
107106
tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
108-
tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
107+
tx_orphan_2_no_fee.vout.append(CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
109108

110109
# A valid transaction with sufficient fee
111110
tx_orphan_2_valid = CTransaction()
112111
tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
113-
tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
112+
tx_orphan_2_valid.vout.append(CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
114113
tx_orphan_2_valid.calc_sha256()
115114

116115
# An invalid transaction with negative fee
@@ -157,13 +156,72 @@ def run_test(self):
157156
with node.assert_debug_log(['orphanage overflow, removed 1 tx']):
158157
node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)
159158

159+
self.log.info('Test orphan with rejected parents')
160160
rejected_parent = CTransaction()
161161
rejected_parent.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
162162
rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
163163
rejected_parent.rehash()
164164
with node.assert_debug_log(['not keeping orphan with rejected parents {}'.format(rejected_parent.hash)]):
165165
node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
166166

167+
self.log.info('Test that a peer disconnection causes erase its transactions from the orphan pool')
168+
with node.assert_debug_log(['Erased 100 orphan tx from peer=25']):
169+
self.reconnect_p2p(num_connections=1)
170+
171+
self.log.info('Test that a transaction in the orphan pool is included in a new tip block causes erase this transaction from the orphan pool')
172+
tx_withhold_until_block_A = CTransaction()
173+
tx_withhold_until_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 1)))
174+
tx_withhold_until_block_A.vout = [CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
175+
tx_withhold_until_block_A.calc_sha256()
176+
177+
tx_orphan_include_by_block_A = CTransaction()
178+
tx_orphan_include_by_block_A.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 0)))
179+
tx_orphan_include_by_block_A.vout.append(CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
180+
tx_orphan_include_by_block_A.calc_sha256()
181+
182+
self.log.info('Send the orphan ... ')
183+
node.p2ps[0].send_txs_and_test([tx_orphan_include_by_block_A], node, success=False)
184+
185+
tip = int(node.getbestblockhash(), 16)
186+
height = node.getblockcount() + 1
187+
block_A = create_block(tip, create_coinbase(height))
188+
block_A.vtx.extend([tx_withhold, tx_withhold_until_block_A, tx_orphan_include_by_block_A])
189+
block_A.hashMerkleRoot = block_A.calc_merkle_root()
190+
block_A.solve()
191+
192+
self.log.info('Send the block that includes the previous orphan ... ')
193+
with node.assert_debug_log(["Erased 1 orphan tx included or conflicted by block"]):
194+
node.p2ps[0].send_blocks_and_test([block_A], node, success=True)
195+
196+
self.log.info('Test that a transaction in the orphan pool conflicts with a new tip block causes erase this transaction from the orphan pool')
197+
tx_withhold_until_block_B = CTransaction()
198+
tx_withhold_until_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 1)))
199+
tx_withhold_until_block_B.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
200+
tx_withhold_until_block_B.calc_sha256()
201+
202+
tx_orphan_include_by_block_B = CTransaction()
203+
tx_orphan_include_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
204+
tx_orphan_include_by_block_B.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
205+
tx_orphan_include_by_block_B.calc_sha256()
206+
207+
tx_orphan_conflict_by_block_B = CTransaction()
208+
tx_orphan_conflict_by_block_B.vin.append(CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
209+
tx_orphan_conflict_by_block_B.vout.append(CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
210+
tx_orphan_conflict_by_block_B.calc_sha256()
211+
self.log.info('Send the orphan ... ')
212+
node.p2ps[0].send_txs_and_test([tx_orphan_conflict_by_block_B], node, success=False)
213+
214+
tip = int(node.getbestblockhash(), 16)
215+
height = node.getblockcount() + 1
216+
block_B = create_block(tip, create_coinbase(height))
217+
block_B.vtx.extend([tx_withhold_until_block_B, tx_orphan_include_by_block_B])
218+
block_B.hashMerkleRoot = block_B.calc_merkle_root()
219+
block_B.solve()
220+
221+
self.log.info('Send the block that includes a transaction which conflicts with the previous orphan ... ')
222+
with node.assert_debug_log(["Erased 1 orphan tx included or conflicted by block"]):
223+
node.p2ps[0].send_blocks_and_test([block_B], node, success=True)
224+
167225

168226
if __name__ == '__main__':
169227
InvalidTxRequestTest().main()

0 commit comments

Comments
 (0)