Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mempool_packages.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2014-2019 The Bitcoin Core developers | # Copyright (c) 2014-2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test descendant package tracking code.""" | """Test descendant package tracking code.""" | ||||
from decimal import Decimal | from decimal import Decimal | ||||
from test_framework.messages import COIN | from test_framework.messages import COIN | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
assert_raises_rpc_error, | assert_raises_rpc_error, | ||||
satoshi_round, | satoshi_round, | ||||
wait_until, | |||||
) | ) | ||||
# default limits | # default limits | ||||
MAX_ANCESTORS = 50 | MAX_ANCESTORS = 50 | ||||
MAX_DESCENDANTS = 50 | MAX_DESCENDANTS = 50 | ||||
# custom limits for node1 | # custom limits for node1 | ||||
MAX_ANCESTORS_CUSTOM = 5 | MAX_ANCESTORS_CUSTOM = 5 | ||||
MAX_DESCENDANTS_CUSTOM = 10 | |||||
assert MAX_DESCENDANTS_CUSTOM >= MAX_ANCESTORS_CUSTOM | |||||
class MempoolPackagesTest(BitcoinTestFramework): | class MempoolPackagesTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
common_params = ["-maxorphantx=1000"] | |||||
self.extra_args = [ | self.extra_args = [ | ||||
[ | common_params, common_params + | ||||
"-maxorphantx=1000", | ["-limitancestorcount={}".format(MAX_ANCESTORS_CUSTOM)]] | ||||
# immediate tx relay | |||||
"-whitelist=noban@127.0.0.1", | |||||
], | |||||
[ | |||||
"-maxorphantx=1000", | |||||
"-limitancestorcount={}".format(MAX_ANCESTORS_CUSTOM), | |||||
"-limitdescendantcount={}".format(MAX_DESCENDANTS_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): | ||||
▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
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 | ||||
# Save sent txs for the purpose of checking node1's mempool later | |||||
# (see below) | |||||
chain = [] | |||||
for i in range(MAX_DESCENDANTS - 1): | for i 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) | ||||
chain.append(txid) | |||||
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(sorted(mempool[parent_transaction] | assert_equal(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(-26, "too-long-mempool-chain", 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) | ||||
# Check that node1's mempool is as expected, containing: | # TODO: check that node1's mempool is as expected | ||||
# - txs from previous ancestor test (-> custom ancestor limit) | |||||
# - parent tx for descendant test | |||||
# - txs chained off parent tx (-> custom descendant limit) | |||||
wait_until(lambda: len(self.nodes[1].getrawmempool(False)) == | |||||
MAX_ANCESTORS_CUSTOM + MAX_DESCENDANTS_CUSTOM, timeout=10) | |||||
mempool0 = self.nodes[0].getrawmempool(False) | |||||
mempool1 = self.nodes[1].getrawmempool(False) | |||||
assert set(mempool1).issubset(set(mempool0)) | |||||
assert parent_transaction in mempool1 | |||||
for tx in chain[:MAX_DESCENDANTS_CUSTOM - 1]: | |||||
assert tx in mempool1 | |||||
for tx in chain[MAX_DESCENDANTS_CUSTOM - 1:]: | |||||
assert tx not in mempool1 | |||||
# TODO: more detailed check of node1's mempool (fees etc.) | |||||
# TODO: test descendant size limits | # TODO: test descendant size limits | ||||
# Test reorg handling | # Test reorg handling | ||||
# First, the basics: | # First, the basics: | ||||
self.nodes[0].generate(1) | self.nodes[0].generate(1) | ||||
self.sync_blocks() | self.sync_blocks() | ||||
self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash()) | self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash()) | ||||
▲ Show 20 Lines • Show All 65 Lines • Show Last 20 Lines |