Changeset View
Changeset View
Standalone View
Standalone View
test/functional/mempool_packages.py
Show All 10 Lines | |||||
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, | ||||
sync_blocks, | sync_blocks, | ||||
sync_mempools, | sync_mempools, | ||||
) | ) | ||||
MAX_ANCESTORS = 25 | # TODO: The activation code can be reverted after the new policy becomes | ||||
MAX_DESCENDANTS = 25 | # active. We don't need to test the old policy anymore then. | ||||
# Phonon dummy activation time | |||||
ACTIVATION_TIME = 2000000000 | |||||
# Replay protection time needs to be moved beyond phonon activation | |||||
REPLAY_PROTECTION_TIME = ACTIVATION_TIME * 2 | |||||
class MempoolPackagesTest(BitcoinTestFramework): | class MempoolPackagesTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 2 | self.num_nodes = 2 | ||||
self.extra_args = [["-maxorphantx=1000"], | |||||
["-maxorphantx=1000", "-limitancestorcount=5"]] | common_params = ["-maxorphantx=1000", | ||||
"-phononactivationtime={}".format(ACTIVATION_TIME), | |||||
"-replayprotectionactivationtime={}".format(REPLAY_PROTECTION_TIME)] | |||||
self.extra_args = [common_params, | |||||
common_params + ["-limitancestorcount=5"]] | |||||
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 i in range(num_outputs): | for i 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): | ||||
# Pre-phonon test | |||||
[n.setmocktime(ACTIVATION_TIME - 10000) for n in self.nodes] | |||||
self.run_mempool_test(max_ancestors=25, max_descendants=25) | |||||
deadalnix: There is already a facility to run test pre and post upgrade. Please use this unless you are… | |||||
# Post-phonon test | |||||
[n.setmocktime(ACTIVATION_TIME) for n in self.nodes] | |||||
self.run_mempool_test(max_ancestors=50, max_descendants=50) | |||||
def run_mempool_test(self, *, max_ancestors, max_descendants): | |||||
# Mine some blocks and have them mature. | # Mine some blocks and have them mature. | ||||
self.nodes[0].generate(101) | self.nodes[0].generate(101) | ||||
utxo = self.nodes[0].listunspent(10) | |||||
# minimumAmount is needed to ensure we get an input with enough coins | |||||
# to generate long transaction chains | |||||
utxo = self.nodes[0].listunspent( | |||||
minconf=10, query_options={ | |||||
'minimumAmount': 50}) | |||||
txid = utxo[0]['txid'] | txid = utxo[0]['txid'] | ||||
vout = utxo[0]['vout'] | vout = utxo[0]['vout'] | ||||
value = utxo[0]['amount'] | value = utxo[0]['amount'] | ||||
fee = Decimal("0.0001") | fee = Decimal("0.0001") | ||||
# MAX_ANCESTORS transactions off a confirmed tx should be fine | # max_ancestors transactions off a confirmed tx should be fine | ||||
chain = [] | chain = [] | ||||
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 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 | ||||
ancestor_size = sum([mempool[tx]['size'] for tx in mempool]) | ancestor_size = sum([mempool[tx]['size'] for tx in mempool]) | ||||
ancestor_count = MAX_ANCESTORS | ancestor_count = max_ancestors | ||||
ancestor_fees = sum([mempool[tx]['fee'] for tx in mempool]) | ancestor_fees = sum([mempool[tx]['fee'] 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]) | ||||
▲ Show 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | def run_mempool_test(self, *, max_ancestors, max_descendants): | ||||
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 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) | ||||
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) | ||||
Show All 20 Lines | def run_mempool_test(self, *, max_ancestors, max_descendants): | ||||
# Tx0 -> Tx1 (vout0) | # Tx0 -> Tx1 (vout0) | ||||
# \--> 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(query_options={'minimumAmount': 50}) | ||||
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 i in range(2): | for i in range(2): | ||||
Show All 40 Lines |
There is already a facility to run test pre and post upgrade. Please use this unless you are specifically testing for the activation.