Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mempool_packages.py
Show All 26 Lines | def set_test_params(self): | ||||
"-maxorphantx=1000", | "-maxorphantx=1000", | ||||
"-deprecatedrpc=mempool_ancestors_descendants", | "-deprecatedrpc=mempool_ancestors_descendants", | ||||
# This test tests mempool ancestor chain limits, which are no longer | # This test tests mempool ancestor chain limits, which are no longer | ||||
# enforced after wellington, so we need to force wellington to | # enforced after wellington, so we need to force wellington to | ||||
# activate in the distant future | # activate in the distant future | ||||
f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}", | f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}", | ||||
] | ] | ||||
self.extra_args = [ | self.extra_args = [ | ||||
common_params, common_params + | common_params, | ||||
[f"-limitancestorcount={MAX_ANCESTORS_CUSTOM}"]] | common_params + [f"-limitancestorcount={MAX_ANCESTORS_CUSTOM}"], | ||||
] | |||||
def skip_test_if_missing_module(self): | def skip_test_if_missing_module(self): | ||||
self.skip_if_no_wallet() | self.skip_if_no_wallet() | ||||
# Build a transaction that spends parent_txid:vout | # Build a transaction that spends parent_txid:vout | ||||
# Return amount sent | # Return amount sent | ||||
def chain_transaction(self, node, parent_txid, vout, | def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs): | ||||
value, fee, num_outputs): | |||||
send_value = satoshi_round((value - fee) / num_outputs) | send_value = satoshi_round((value - fee) / num_outputs) | ||||
inputs = [{'txid': parent_txid, 'vout': vout}] | inputs = [{"txid": parent_txid, "vout": vout}] | ||||
outputs = {} | outputs = {} | ||||
for _ in range(num_outputs): | for _ in range(num_outputs): | ||||
outputs[node.getnewaddress()] = send_value | outputs[node.getnewaddress()] = send_value | ||||
rawtx = node.createrawtransaction(inputs, outputs) | rawtx = node.createrawtransaction(inputs, outputs) | ||||
signedtx = node.signrawtransactionwithwallet(rawtx) | signedtx = node.signrawtransactionwithwallet(rawtx) | ||||
txid = node.sendrawtransaction(signedtx['hex']) | txid = node.sendrawtransaction(signedtx["hex"]) | ||||
fulltx = node.getrawtransaction(txid, 1) | fulltx = node.getrawtransaction(txid, 1) | ||||
# make sure we didn't generate a change output | # make sure we didn't generate a change output | ||||
assert len(fulltx['vout']) == num_outputs | assert len(fulltx["vout"]) == num_outputs | ||||
return (txid, send_value) | return (txid, send_value) | ||||
def run_test(self): | def run_test(self): | ||||
# Mine some blocks and have them mature. | # Mine some blocks and have them mature. | ||||
# keep track of invs | # keep track of invs | ||||
peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) | peer_inv_store = self.nodes[0].add_p2p_connection(P2PTxInvStore()) | ||||
self.generate(self.nodes[0], 101) | self.generate(self.nodes[0], 101) | ||||
utxo = self.nodes[0].listunspent(10) | utxo = self.nodes[0].listunspent(10) | ||||
txid = utxo[0]['txid'] | txid = utxo[0]["txid"] | ||||
vout = utxo[0]['vout'] | vout = utxo[0]["vout"] | ||||
value = utxo[0]['amount'] | value = utxo[0]["amount"] | ||||
assert 'ancestorcount' not in utxo[0] | assert "ancestorcount" not in utxo[0] | ||||
assert 'ancestorsize' not in utxo[0] | assert "ancestorsize" not in utxo[0] | ||||
assert 'ancestorfees' not in utxo[0] | assert "ancestorfees" not in utxo[0] | ||||
fee = Decimal("100") | fee = Decimal("100") | ||||
# MAX_ANCESTORS transactions off a confirmed tx should be fine | # MAX_ANCESTORS transactions off a confirmed tx should be fine | ||||
chain = [] | chain = [] | ||||
ancestor_size = 0 | ancestor_size = 0 | ||||
ancestor_fees = Decimal(0) | ancestor_fees = Decimal(0) | ||||
for i in range(MAX_ANCESTORS): | for i in range(MAX_ANCESTORS): | ||||
(txid, sent_value) = self.chain_transaction( | (txid, sent_value) = self.chain_transaction( | ||||
self.nodes[0], txid, 0, value, fee, 1) | self.nodes[0], txid, 0, value, fee, 1 | ||||
) | |||||
value = sent_value | value = sent_value | ||||
chain.append(txid) | chain.append(txid) | ||||
# Check that listunspent ancestor{count, size, fees} yield the | # Check that listunspent ancestor{count, size, fees} yield the | ||||
# correct results | # correct results | ||||
wallet_unspent = self.nodes[0].listunspent(minconf=0) | wallet_unspent = self.nodes[0].listunspent(minconf=0) | ||||
this_unspent = next( | this_unspent = next( | ||||
utxo_info for utxo_info in wallet_unspent if utxo_info['txid'] == txid) | utxo_info for utxo_info in wallet_unspent if utxo_info["txid"] == txid | ||||
assert_equal(this_unspent['ancestorcount'], i + 1) | ) | ||||
ancestor_size += self.nodes[0].getrawtransaction( | assert_equal(this_unspent["ancestorcount"], i + 1) | ||||
txid=txid, verbose=True)['size'] | ancestor_size += self.nodes[0].getrawtransaction(txid=txid, verbose=True)[ | ||||
assert_equal(this_unspent['ancestorsize'], ancestor_size) | "size" | ||||
ancestor_fees -= self.nodes[0].gettransaction(txid=txid)['fee'] | ] | ||||
assert_equal(this_unspent['ancestorfees'], ancestor_fees) | assert_equal(this_unspent["ancestorsize"], ancestor_size) | ||||
ancestor_fees -= self.nodes[0].gettransaction(txid=txid)["fee"] | |||||
assert_equal(this_unspent["ancestorfees"], ancestor_fees) | |||||
# Wait until mempool transactions have passed initial broadcast | # Wait until mempool transactions have passed initial broadcast | ||||
# (sent inv and received getdata) | # (sent inv and received getdata) | ||||
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if | # Otherwise, getrawmempool may be inconsistent with getmempoolentry if | ||||
# unbroadcast changes in between | # unbroadcast changes in between | ||||
peer_inv_store.wait_for_broadcast(chain) | peer_inv_store.wait_for_broadcast(chain) | ||||
# Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor | # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor | ||||
# count and fees should look correct | # count and fees should look correct | ||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
assert_equal(len(mempool), MAX_ANCESTORS) | assert_equal(len(mempool), MAX_ANCESTORS) | ||||
descendant_count = 1 | descendant_count = 1 | ||||
descendant_fees = 0 | descendant_fees = 0 | ||||
descendant_size = 0 | descendant_size = 0 | ||||
assert_equal(ancestor_size, | assert_equal(ancestor_size, sum([mempool[tx]["size"] for tx in mempool])) | ||||
sum([mempool[tx]['size'] for tx in mempool])) | |||||
ancestor_count = MAX_ANCESTORS | ancestor_count = MAX_ANCESTORS | ||||
assert_equal(ancestor_fees, | assert_equal( | ||||
sum([mempool[tx]['fees']['base'] for tx in mempool])) | ancestor_fees, sum([mempool[tx]["fees"]["base"] for tx in mempool]) | ||||
) | |||||
descendants = [] | descendants = [] | ||||
ancestors = list(chain) | ancestors = list(chain) | ||||
for x in reversed(chain): | for x in reversed(chain): | ||||
# Check that getmempoolentry is consistent with getrawmempool | # Check that getmempoolentry is consistent with getrawmempool | ||||
entry = self.nodes[0].getmempoolentry(x) | entry = self.nodes[0].getmempoolentry(x) | ||||
assert_equal(entry, mempool[x]) | assert_equal(entry, mempool[x]) | ||||
# Check that the descendant calculations are correct | # Check that the descendant calculations are correct | ||||
assert_equal(mempool[x]['descendantcount'], descendant_count) | assert_equal(mempool[x]["descendantcount"], descendant_count) | ||||
descendant_fees += mempool[x]['fees']['base'] | descendant_fees += mempool[x]["fees"]["base"] | ||||
assert_equal( | assert_equal(mempool[x]["fees"]["modified"], mempool[x]["fees"]["base"]) | ||||
mempool[x]['fees']['modified'], | assert_equal(mempool[x]["fees"]["descendant"], descendant_fees) | ||||
mempool[x]['fees']['base']) | descendant_size += mempool[x]["size"] | ||||
assert_equal(mempool[x]['fees']['descendant'], descendant_fees) | assert_equal(mempool[x]["descendantsize"], descendant_size) | ||||
descendant_size += mempool[x]['size'] | |||||
assert_equal(mempool[x]['descendantsize'], descendant_size) | |||||
descendant_count += 1 | descendant_count += 1 | ||||
# Check that ancestor calculations are correct | # Check that ancestor calculations are correct | ||||
assert_equal(mempool[x]['ancestorcount'], ancestor_count) | assert_equal(mempool[x]["ancestorcount"], ancestor_count) | ||||
assert_equal(mempool[x]['fees']['ancestor'], ancestor_fees) | assert_equal(mempool[x]["fees"]["ancestor"], ancestor_fees) | ||||
assert_equal(mempool[x]['ancestorsize'], ancestor_size) | assert_equal(mempool[x]["ancestorsize"], ancestor_size) | ||||
ancestor_size -= mempool[x]['size'] | ancestor_size -= mempool[x]["size"] | ||||
ancestor_fees -= mempool[x]['fees']['base'] | ancestor_fees -= mempool[x]["fees"]["base"] | ||||
ancestor_count -= 1 | ancestor_count -= 1 | ||||
# Check that parent/child list is correct | # Check that parent/child list is correct | ||||
assert_equal(mempool[x]['spentby'], descendants[-1:]) | assert_equal(mempool[x]["spentby"], descendants[-1:]) | ||||
assert_equal(mempool[x]['depends'], ancestors[-2:-1]) | assert_equal(mempool[x]["depends"], ancestors[-2:-1]) | ||||
# Check that getmempooldescendants is correct | # Check that getmempooldescendants is correct | ||||
assert_equal(sorted(descendants), sorted( | assert_equal( | ||||
self.nodes[0].getmempooldescendants(x))) | sorted(descendants), sorted(self.nodes[0].getmempooldescendants(x)) | ||||
) | |||||
# Check getmempooldescendants verbose output is correct | # Check getmempooldescendants verbose output is correct | ||||
for descendant, dinfo in self.nodes[0].getmempooldescendants( | for descendant, dinfo in ( | ||||
x, True).items(): | self.nodes[0].getmempooldescendants(x, True).items() | ||||
assert_equal(dinfo['depends'], [ | ): | ||||
chain[chain.index(descendant) - 1]]) | assert_equal(dinfo["depends"], [chain[chain.index(descendant) - 1]]) | ||||
if dinfo['descendantcount'] > 1: | if dinfo["descendantcount"] > 1: | ||||
assert_equal(dinfo['spentby'], [ | assert_equal(dinfo["spentby"], [chain[chain.index(descendant) + 1]]) | ||||
chain[chain.index(descendant) + 1]]) | |||||
else: | else: | ||||
assert_equal(dinfo['spentby'], []) | assert_equal(dinfo["spentby"], []) | ||||
descendants.append(x) | descendants.append(x) | ||||
# Check that getmempoolancestors is correct | # Check that getmempoolancestors is correct | ||||
ancestors.remove(x) | ancestors.remove(x) | ||||
assert_equal(sorted(ancestors), sorted( | assert_equal( | ||||
self.nodes[0].getmempoolancestors(x))) | sorted(ancestors), sorted(self.nodes[0].getmempoolancestors(x)) | ||||
) | |||||
# Check that getmempoolancestors verbose output is correct | # Check that getmempoolancestors verbose output is correct | ||||
for ancestor, ainfo in self.nodes[0].getmempoolancestors( | for ancestor, ainfo in self.nodes[0].getmempoolancestors(x, True).items(): | ||||
x, True).items(): | assert_equal(ainfo["spentby"], [chain[chain.index(ancestor) + 1]]) | ||||
assert_equal(ainfo['spentby'], [ | if ainfo["ancestorcount"] > 1: | ||||
chain[chain.index(ancestor) + 1]]) | assert_equal(ainfo["depends"], [chain[chain.index(ancestor) - 1]]) | ||||
if ainfo['ancestorcount'] > 1: | |||||
assert_equal(ainfo['depends'], [ | |||||
chain[chain.index(ancestor) - 1]]) | |||||
else: | else: | ||||
assert_equal(ainfo['depends'], []) | assert_equal(ainfo["depends"], []) | ||||
# Check that getmempoolancestors/getmempooldescendants correctly handle | # Check that getmempoolancestors/getmempooldescendants correctly handle | ||||
# verbose=true | # verbose=true | ||||
v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True) | v_ancestors = self.nodes[0].getmempoolancestors(chain[-1], True) | ||||
assert_equal(len(v_ancestors), len(chain) - 1) | assert_equal(len(v_ancestors), len(chain) - 1) | ||||
for x in v_ancestors.keys(): | for x in v_ancestors.keys(): | ||||
assert_equal(mempool[x], v_ancestors[x]) | assert_equal(mempool[x], v_ancestors[x]) | ||||
assert chain[-1] not in v_ancestors.keys() | assert chain[-1] not in v_ancestors.keys() | ||||
v_descendants = self.nodes[0].getmempooldescendants(chain[0], True) | v_descendants = self.nodes[0].getmempooldescendants(chain[0], True) | ||||
assert_equal(len(v_descendants), len(chain) - 1) | assert_equal(len(v_descendants), len(chain) - 1) | ||||
for x in v_descendants.keys(): | for x in v_descendants.keys(): | ||||
assert_equal(mempool[x], v_descendants[x]) | assert_equal(mempool[x], v_descendants[x]) | ||||
assert chain[0] not in v_descendants.keys() | assert chain[0] not in v_descendants.keys() | ||||
# Check that ancestor modified fees includes fee deltas from | # Check that ancestor modified fees includes fee deltas from | ||||
# prioritisetransaction | # prioritisetransaction | ||||
self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000) | self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000) | ||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
ancestor_fees = 0 | ancestor_fees = 0 | ||||
for x in chain: | for x in chain: | ||||
ancestor_fees += mempool[x]['fees']['base'] | ancestor_fees += mempool[x]["fees"]["base"] | ||||
assert_equal(mempool[x]['fees']['ancestor'], | assert_equal( | ||||
ancestor_fees + Decimal('10.00')) | mempool[x]["fees"]["ancestor"], ancestor_fees + Decimal("10.00") | ||||
) | |||||
# Undo the prioritisetransaction for later tests | # Undo the prioritisetransaction for later tests | ||||
self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000) | self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000) | ||||
# Check that descendant modified fees includes fee deltas from | # Check that descendant modified fees includes fee deltas from | ||||
# prioritisetransaction | # prioritisetransaction | ||||
self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000) | self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000) | ||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
descendant_fees = 0 | descendant_fees = 0 | ||||
for x in reversed(chain): | for x in reversed(chain): | ||||
descendant_fees += mempool[x]['fees']['base'] | descendant_fees += mempool[x]["fees"]["base"] | ||||
assert_equal(mempool[x]['fees']['descendant'], | assert_equal( | ||||
descendant_fees + Decimal('10.00')) | mempool[x]["fees"]["descendant"], descendant_fees + Decimal("10.00") | ||||
) | |||||
# Adding one more transaction on to the chain should fail. | # Adding one more transaction on to the chain should fail. | ||||
assert_raises_rpc_error(-26, "too-long-mempool-chain", | assert_raises_rpc_error( | ||||
self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1) | -26, | ||||
"too-long-mempool-chain", | |||||
self.chain_transaction, | |||||
self.nodes[0], | |||||
txid, | |||||
vout, | |||||
value, | |||||
fee, | |||||
1, | |||||
) | |||||
# Check that prioritising a tx before it's added to the mempool works | # Check that prioritising a tx before it's added to the mempool works | ||||
# First clear the mempool by mining a block. | # First clear the mempool by mining a block. | ||||
self.generate(self.nodes[0], 1) | self.generate(self.nodes[0], 1) | ||||
assert_equal(len(self.nodes[0].getrawmempool()), 0) | assert_equal(len(self.nodes[0].getrawmempool()), 0) | ||||
# Prioritise a transaction that has been mined, then add it back to the | # Prioritise a transaction that has been mined, then add it back to the | ||||
# mempool by using invalidateblock. | # mempool by using invalidateblock. | ||||
self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000) | self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=2000) | ||||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | ||||
# Keep node1's tip synced with node0 | # Keep node1's tip synced with node0 | ||||
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) | self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) | ||||
# Now check that the transaction is in the mempool, with the right | # Now check that the transaction is in the mempool, with the right | ||||
# modified fee | # modified fee | ||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
descendant_fees = 0 | descendant_fees = 0 | ||||
for x in reversed(chain): | for x in reversed(chain): | ||||
descendant_fees += mempool[x]['fees']['base'] | descendant_fees += mempool[x]["fees"]["base"] | ||||
if (x == chain[-1]): | if x == chain[-1]: | ||||
assert_equal(mempool[x]['fees']['modified'], | assert_equal( | ||||
mempool[x]['fees']['base'] + satoshi_round(20.00)) | mempool[x]["fees"]["modified"], | ||||
assert_equal(mempool[x]['fees']['descendant'], | mempool[x]["fees"]["base"] + satoshi_round(20.00), | ||||
descendant_fees + satoshi_round(20.00)) | ) | ||||
assert_equal( | |||||
mempool[x]["fees"]["descendant"], descendant_fees + satoshi_round(20.00) | |||||
) | |||||
# Check that node1's mempool is as expected (-> custom ancestor limit) | # Check that node1's mempool is as expected (-> custom ancestor limit) | ||||
mempool0 = self.nodes[0].getrawmempool(False) | mempool0 = self.nodes[0].getrawmempool(False) | ||||
mempool1 = self.nodes[1].getrawmempool(False) | mempool1 = self.nodes[1].getrawmempool(False) | ||||
assert_equal(len(mempool1), MAX_ANCESTORS_CUSTOM) | assert_equal(len(mempool1), MAX_ANCESTORS_CUSTOM) | ||||
assert set(mempool1).issubset(set(mempool0)) | assert set(mempool1).issubset(set(mempool0)) | ||||
for tx in chain[:MAX_ANCESTORS_CUSTOM]: | for tx in chain[:MAX_ANCESTORS_CUSTOM]: | ||||
assert tx in mempool1 | assert tx in mempool1 | ||||
# TODO: more detailed check of node1's mempool (fees etc.) | # TODO: more detailed check of node1's mempool (fees etc.) | ||||
# check transaction unbroadcast info (should be false if in both | # check transaction unbroadcast info (should be false if in both | ||||
# mempools) | # mempools) | ||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
for tx in mempool: | for tx in mempool: | ||||
assert_equal(mempool[tx]['unbroadcast'], False) | assert_equal(mempool[tx]["unbroadcast"], False) | ||||
# TODO: test ancestor size limits | # TODO: test ancestor size limits | ||||
# Now test descendant chain limits | # Now test descendant chain limits | ||||
txid = utxo[1]['txid'] | txid = utxo[1]["txid"] | ||||
value = utxo[1]['amount'] | value = utxo[1]["amount"] | ||||
vout = utxo[1]['vout'] | vout = utxo[1]["vout"] | ||||
transaction_package = [] | transaction_package = [] | ||||
tx_children = [] | tx_children = [] | ||||
# First create one parent tx with 10 children | # First create one parent tx with 10 children | ||||
(txid, sent_value) = self.chain_transaction( | (txid, sent_value) = self.chain_transaction( | ||||
self.nodes[0], txid, vout, value, fee, 10) | self.nodes[0], txid, vout, value, fee, 10 | ||||
) | |||||
parent_transaction = txid | parent_transaction = txid | ||||
for i in range(10): | for i in range(10): | ||||
transaction_package.append( | transaction_package.append({"txid": txid, "vout": i, "amount": sent_value}) | ||||
{'txid': txid, 'vout': i, 'amount': sent_value}) | |||||
# Sign and send up to MAX_DESCENDANT transactions chained off the | # Sign and send up to MAX_DESCENDANT transactions chained off the | ||||
# parent tx | # parent tx | ||||
for _ in range(MAX_DESCENDANTS - 1): | for _ in range(MAX_DESCENDANTS - 1): | ||||
utxo = transaction_package.pop(0) | utxo = transaction_package.pop(0) | ||||
(txid, sent_value) = self.chain_transaction( | (txid, sent_value) = self.chain_transaction( | ||||
self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) | self.nodes[0], utxo["txid"], utxo["vout"], utxo["amount"], fee, 10 | ||||
if utxo['txid'] is parent_transaction: | ) | ||||
if utxo["txid"] is parent_transaction: | |||||
tx_children.append(txid) | tx_children.append(txid) | ||||
for j in range(10): | for j in range(10): | ||||
transaction_package.append( | transaction_package.append( | ||||
{'txid': txid, 'vout': j, 'amount': sent_value}) | {"txid": txid, "vout": j, "amount": sent_value} | ||||
) | |||||
mempool = self.nodes[0].getrawmempool(True) | mempool = self.nodes[0].getrawmempool(True) | ||||
assert_equal(mempool[parent_transaction] | assert_equal(mempool[parent_transaction]["descendantcount"], MAX_DESCENDANTS) | ||||
['descendantcount'], MAX_DESCENDANTS) | assert_equal( | ||||
assert_equal(sorted(mempool[parent_transaction] | sorted(mempool[parent_transaction]["spentby"]), sorted(tx_children) | ||||
['spentby']), sorted(tx_children)) | ) | ||||
for child in tx_children: | for child in tx_children: | ||||
assert_equal(mempool[child]['depends'], [parent_transaction]) | assert_equal(mempool[child]["depends"], [parent_transaction]) | ||||
# Sending one more chained transaction will fail | # Sending one more chained transaction will fail | ||||
utxo = transaction_package.pop(0) | utxo = transaction_package.pop(0) | ||||
assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, | assert_raises_rpc_error( | ||||
self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) | -26, | ||||
"too-long-mempool-chain", | |||||
self.chain_transaction, | |||||
self.nodes[0], | |||||
utxo["txid"], | |||||
utxo["vout"], | |||||
utxo["amount"], | |||||
fee, | |||||
10, | |||||
) | |||||
# TODO: check that node1's mempool is as expected | # TODO: check that node1's mempool is as expected | ||||
# TODO: test descendant size limits | # TODO: test descendant size limits | ||||
# Test reorg handling | # Test reorg handling | ||||
# First, the basics: | # First, the basics: | ||||
self.generate(self.nodes[0], 1) | self.generate(self.nodes[0], 1) | ||||
Show All 10 Lines | def run_test(self): | ||||
# \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7 | # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7 | ||||
# | # | ||||
# Mine them in the next block, then generate a new tx8 that spends | # Mine them in the next block, then generate a new tx8 that spends | ||||
# Tx1 and Tx7, and add to node1's mempool, then disconnect the | # Tx1 and Tx7, and add to node1's mempool, then disconnect the | ||||
# last block. | # last block. | ||||
# Create tx0 with 2 outputs | # Create tx0 with 2 outputs | ||||
utxo = self.nodes[0].listunspent() | utxo = self.nodes[0].listunspent() | ||||
txid = utxo[0]['txid'] | txid = utxo[0]["txid"] | ||||
value = utxo[0]['amount'] | value = utxo[0]["amount"] | ||||
vout = utxo[0]['vout'] | vout = utxo[0]["vout"] | ||||
send_value = satoshi_round((value - fee) / 2) | send_value = satoshi_round((value - fee) / 2) | ||||
inputs = [{'txid': txid, 'vout': vout}] | inputs = [{"txid": txid, "vout": vout}] | ||||
outputs = {} | outputs = {} | ||||
for _ in range(2): | for _ in range(2): | ||||
outputs[self.nodes[0].getnewaddress()] = send_value | outputs[self.nodes[0].getnewaddress()] = send_value | ||||
rawtx = self.nodes[0].createrawtransaction(inputs, outputs) | rawtx = self.nodes[0].createrawtransaction(inputs, outputs) | ||||
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | ||||
txid = self.nodes[0].sendrawtransaction(signedtx['hex']) | txid = self.nodes[0].sendrawtransaction(signedtx["hex"]) | ||||
tx0_id = txid | tx0_id = txid | ||||
value = send_value | value = send_value | ||||
# Create tx1 | # Create tx1 | ||||
tx1_id, _ = self.chain_transaction( | tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) | ||||
self.nodes[0], tx0_id, 0, value, fee, 1) | |||||
# Create tx2-7 | # Create tx2-7 | ||||
vout = 1 | vout = 1 | ||||
txid = tx0_id | txid = tx0_id | ||||
for _ in range(6): | for _ in range(6): | ||||
(txid, sent_value) = self.chain_transaction( | (txid, sent_value) = self.chain_transaction( | ||||
self.nodes[0], txid, vout, value, fee, 1) | self.nodes[0], txid, vout, value, fee, 1 | ||||
) | |||||
vout = 0 | vout = 0 | ||||
value = sent_value | value = sent_value | ||||
# Mine these in a block | # Mine these in a block | ||||
self.generate(self.nodes[0], 1) | self.generate(self.nodes[0], 1) | ||||
# Now generate tx8, with a big fee | # Now generate tx8, with a big fee | ||||
inputs = [{'txid': tx1_id, 'vout': 0}, {'txid': txid, 'vout': 0}] | inputs = [{"txid": tx1_id, "vout": 0}, {"txid": txid, "vout": 0}] | ||||
outputs = {self.nodes[0].getnewaddress(): send_value + value - 4 * fee} | outputs = {self.nodes[0].getnewaddress(): send_value + value - 4 * fee} | ||||
rawtx = self.nodes[0].createrawtransaction(inputs, outputs) | rawtx = self.nodes[0].createrawtransaction(inputs, outputs) | ||||
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx) | ||||
txid = self.nodes[0].sendrawtransaction(signedtx['hex']) | txid = self.nodes[0].sendrawtransaction(signedtx["hex"]) | ||||
self.sync_mempools() | self.sync_mempools() | ||||
# Now try to disconnect the tip on each node... | # Now try to disconnect the tip on each node... | ||||
self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) | self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash()) | ||||
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash()) | ||||
self.sync_blocks() | self.sync_blocks() | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
MempoolPackagesTest().main() | MempoolPackagesTest().main() |