Skip to content

Commit f4052aa

Browse files
committed
Add test showing bug in mempool packages
coming from btc@598b25d5ee9c08947a52824f47531208943a3c65
1 parent 691749f commit f4052aa

File tree

1 file changed

+83
-12
lines changed

1 file changed

+83
-12
lines changed

test/functional/mempool_packages.py

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
Decimal,
1313
ROUND_DOWN,
1414
JSONRPCException,
15+
sync_blocks,
16+
sync_mempools
1517
)
1618

1719
def satoshi_round(amount):
@@ -22,21 +24,21 @@ def satoshi_round(amount):
2224

2325
class 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+
111182
if __name__ == '__main__':
112183
MempoolPackagesTest().main()

0 commit comments

Comments
 (0)