Skip to content

Commit 598b25d

Browse files
committed
Add test showing bug in mempool packages
1 parent 999c8be commit 598b25d

File tree

1 file changed

+82
-11
lines changed

1 file changed

+82
-11
lines changed

qa/rpc-tests/mempool_packages.py

Lines changed: 82 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,24 @@ class MempoolPackagesTest(BitcoinTestFramework):
1515

1616
def setup_network(self):
1717
self.nodes = []
18-
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0"]))
18+
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-debug"]))
19+
self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-limitancestorcount=5", "-debug"]))
20+
connect_nodes(self.nodes[0], 1)
1921
self.is_network_split = False
2022
self.sync_all()
2123

2224
# Build a transaction that spends parent_txid:vout
2325
# Return amount sent
24-
def chain_transaction(self, parent_txid, vout, value, fee, num_outputs):
26+
def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs):
2527
send_value = satoshi_round((value - fee)/num_outputs)
2628
inputs = [ {'txid' : parent_txid, 'vout' : vout} ]
2729
outputs = {}
2830
for i in xrange(num_outputs):
29-
outputs[self.nodes[0].getnewaddress()] = send_value
30-
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
31-
signedtx = self.nodes[0].signrawtransaction(rawtx)
32-
txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
33-
fulltx = self.nodes[0].getrawtransaction(txid, 1)
31+
outputs[node.getnewaddress()] = send_value
32+
rawtx = node.createrawtransaction(inputs, outputs)
33+
signedtx = node.signrawtransaction(rawtx)
34+
txid = node.sendrawtransaction(signedtx['hex'])
35+
fulltx = node.getrawtransaction(txid, 1)
3436
assert(len(fulltx['vout']) == num_outputs) # make sure we didn't generate a change output
3537
return (txid, send_value)
3638

@@ -46,7 +48,7 @@ def run_test(self):
4648
# 100 transactions off a confirmed tx should be fine
4749
chain = []
4850
for i in xrange(100):
49-
(txid, sent_value) = self.chain_transaction(txid, 0, value, fee, 1)
51+
(txid, sent_value) = self.chain_transaction(self.nodes[0], txid, 0, value, fee, 1)
5052
value = sent_value
5153
chain.append(txid)
5254

@@ -69,10 +71,12 @@ def run_test(self):
6971

7072
# Adding one more transaction on to the chain should fail.
7173
try:
72-
self.chain_transaction(txid, vout, value, fee, 1)
74+
self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
7375
except JSONRPCException as e:
7476
print "too-long-ancestor-chain successfully rejected"
7577

78+
# TODO: check that node1's mempool is as expected
79+
7680
# TODO: test ancestor size limits
7781

7882
# Now test descendant chain limits
@@ -82,15 +86,15 @@ def run_test(self):
8286

8387
transaction_package = []
8488
# First create one parent tx with 10 children
85-
(txid, sent_value) = self.chain_transaction(txid, vout, value, fee, 10)
89+
(txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 10)
8690
parent_transaction = txid
8791
for i in xrange(10):
8892
transaction_package.append({'txid': txid, 'vout': i, 'amount': sent_value})
8993

9094
for i in xrange(1000):
9195
utxo = transaction_package.pop(0)
9296
try:
93-
(txid, sent_value) = self.chain_transaction(utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
97+
(txid, sent_value) = self.chain_transaction(self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10)
9498
for j in xrange(10):
9599
transaction_package.append({'txid': txid, 'vout': j, 'amount': sent_value})
96100
if i == 998:
@@ -101,7 +105,74 @@ def run_test(self):
101105
assert_equal(i, 999)
102106
print "tx that would create too large descendant package successfully rejected"
103107

108+
# TODO: check that node1's mempool is as expected
109+
104110
# TODO: test descendant size limits
105111

112+
# Test reorg handling
113+
# First, the basics:
114+
self.nodes[0].generate(1)
115+
sync_blocks(self.nodes)
116+
self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
117+
self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())
118+
119+
# Now test the case where node1 has a transaction T in its mempool that
120+
# depends on transactions A and B which are in a mined block, and the
121+
# block containing A and B is disconnected, AND B is not accepted back
122+
# into node1's mempool because its ancestor count is too high.
123+
124+
# Create 8 transactions, like so:
125+
# Tx0 -> Tx1 (vout0)
126+
# \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
127+
#
128+
# Mine them in the next block, then generate a new tx8 that spends
129+
# Tx1 and Tx7, and add to node1's mempool, then disconnect the
130+
# last block.
131+
132+
# Create tx0 with 2 outputs
133+
utxo = self.nodes[0].listunspent()
134+
txid = utxo[0]['txid']
135+
value = utxo[0]['amount']
136+
vout = utxo[0]['vout']
137+
138+
send_value = satoshi_round((value - fee)/2)
139+
inputs = [ {'txid' : txid, 'vout' : vout} ]
140+
outputs = {}
141+
for i in xrange(2):
142+
outputs[self.nodes[0].getnewaddress()] = send_value
143+
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
144+
signedtx = self.nodes[0].signrawtransaction(rawtx)
145+
txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
146+
tx0_id = txid
147+
value = send_value
148+
149+
# Create tx1
150+
(tx1_id, tx1_value) = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
151+
152+
# Create tx2-7
153+
vout = 1
154+
txid = tx0_id
155+
for i in xrange(6):
156+
(txid, sent_value) = self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
157+
vout = 0
158+
value = sent_value
159+
160+
# Mine these in a block
161+
self.nodes[0].generate(1)
162+
self.sync_all()
163+
164+
# Now generate tx8, with a big fee
165+
inputs = [ {'txid' : tx1_id, 'vout': 0}, {'txid' : txid, 'vout': 0} ]
166+
outputs = { self.nodes[0].getnewaddress() : send_value + value - 4*fee }
167+
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
168+
signedtx = self.nodes[0].signrawtransaction(rawtx)
169+
txid = self.nodes[0].sendrawtransaction(signedtx['hex'])
170+
sync_mempools(self.nodes)
171+
172+
# Now try to disconnect the tip on each node...
173+
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
174+
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
175+
sync_blocks(self.nodes)
176+
106177
if __name__ == '__main__':
107178
MempoolPackagesTest().main()

0 commit comments

Comments
 (0)