2016-05-06 11:23:48 +02:00
|
|
|
#!/usr/bin/env python3
|
2023-08-16 19:27:31 +02:00
|
|
|
# Copyright (c) 2014-2020 The Bitcoin Core developers
|
2014-11-24 21:18:05 +01:00
|
|
|
# Distributed under the MIT software license, see the accompanying
|
|
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2019-01-07 10:55:35 +01:00
|
|
|
"""Test mempool re-org scenarios.
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2019-01-07 10:55:35 +01:00
|
|
|
Test re-org scenarios with a mempool that contains transactions
|
|
|
|
that spend (directly or indirectly) coinbase transactions.
|
|
|
|
"""
|
2014-11-24 21:18:05 +01:00
|
|
|
|
Merge #13054: tests: Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
68400d8b96 tests: Use explicit imports (practicalswift)
Pull request description:
Enable automatic detection of undefined names in Python tests scripts. Remove wildcard imports.
Wildcard imports make it unclear which names are present in the namespace, confusing both readers and many automated tools.
An additional benefit of not using wildcard imports in tests scripts is that readers of a test script then can infer the rough testing scope just by looking at the imports.
Before this commit:
```
$ contrib/devtools/lint-python.sh | head -10
./test/functional/feature_rbf.py:8:1: F403 'from test_framework.util import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:9:1: F403 'from test_framework.script import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:10:1: F403 'from test_framework.mininode import *' used; unable to detect undefined names
./test/functional/feature_rbf.py:15:12: F405 bytes_to_hex_str may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:17:58: F405 CScript may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:25:13: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:31: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:26:60: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:41: F405 satoshi_round may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
./test/functional/feature_rbf.py:30:68: F405 COIN may be undefined, or defined from star imports: test_framework.mininode, test_framework.script, test_framework.util
$
```
After this commit:
```
$ contrib/devtools/lint-python.sh | head -10
$
```
Tree-SHA512: 3f826d39cffb6438388e5efcb20a9622ff8238247e882d68f7b38609877421b2a8e10e9229575f8eb6a8fa42dec4256986692e92922c86171f750a0e887438d9
2018-08-13 14:24:43 +02:00
|
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
|
|
from test_framework.util import assert_equal, assert_raises_rpc_error
|
2021-06-01 15:04:55 +02:00
|
|
|
from test_framework.wallet import MiniWallet
|
2018-08-11 12:45:29 +02:00
|
|
|
|
2014-11-24 21:18:05 +01:00
|
|
|
class MempoolCoinbaseTest(BitcoinTestFramework):
|
2017-09-01 18:47:13 +02:00
|
|
|
def set_test_params(self):
|
2016-05-20 15:16:51 +02:00
|
|
|
self.num_nodes = 2
|
2020-04-24 13:57:41 +02:00
|
|
|
self.extra_args = [
|
|
|
|
[
|
|
|
|
'-whitelist=noban@127.0.0.1', # immediate tx relay
|
|
|
|
],
|
|
|
|
[]
|
|
|
|
]
|
2014-11-24 21:18:05 +01:00
|
|
|
|
|
|
|
def run_test(self):
|
2021-06-01 15:04:55 +02:00
|
|
|
wallet = MiniWallet(self.nodes[0])
|
|
|
|
|
2017-03-16 11:57:09 +01:00
|
|
|
# Start with a 200 block chain
|
|
|
|
assert_equal(self.nodes[0].getblockcount(), 200)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Add 4 coinbase utxos to the miniwallet")
|
|
|
|
# Block 76 contains the first spendable coinbase txs.
|
|
|
|
first_block = 76
|
|
|
|
wallet.scan_blocks(start=first_block, num=4)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
|
|
|
# Three scenarios for re-orging coinbase spends in the memory pool:
|
2021-06-01 15:04:55 +02:00
|
|
|
# 1. Direct coinbase spend : spend_1
|
|
|
|
# 2. Indirect (coinbase spend in chain, child in mempool) : spend_2 and spend_2_1
|
|
|
|
# 3. Indirect (coinbase and child both in chain) : spend_3 and spend_3_1
|
|
|
|
# Use invalidateblock to make all of the above coinbase spends invalid (immature coinbase),
|
2014-11-24 21:18:05 +01:00
|
|
|
# and make sure the mempool code behaves correctly.
|
2021-06-01 15:04:55 +02:00
|
|
|
b = [self.nodes[0].getblockhash(n) for n in range(first_block, first_block+4)]
|
2020-04-24 13:57:41 +02:00
|
|
|
coinbase_txids = [self.nodes[0].getblock(h)['tx'][0] for h in b]
|
2021-06-01 15:04:55 +02:00
|
|
|
utxo_1 = wallet.get_utxo(txid=coinbase_txids[1])
|
|
|
|
utxo_2 = wallet.get_utxo(txid=coinbase_txids[2])
|
|
|
|
utxo_3 = wallet.get_utxo(txid=coinbase_txids[3])
|
|
|
|
self.log.info("Create three transactions spending from coinbase utxos: spend_1, spend_2, spend_3")
|
|
|
|
spend_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_1)
|
|
|
|
spend_2 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_2)
|
|
|
|
spend_3 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=utxo_3)
|
|
|
|
|
|
|
|
self.log.info("Create another transaction which is time-locked to two blocks in the future")
|
|
|
|
utxo = wallet.get_utxo(txid=coinbase_txids[0])
|
|
|
|
timelock_tx = wallet.create_self_transfer(
|
|
|
|
from_node=self.nodes[0],
|
|
|
|
utxo_to_spend=utxo,
|
|
|
|
mempool_valid=False,
|
|
|
|
locktime=self.nodes[0].getblockcount() + 2
|
|
|
|
)['hex']
|
|
|
|
|
|
|
|
self.log.info("Check that the time-locked transaction is too immature to spend")
|
2019-09-25 11:34:51 +02:00
|
|
|
assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Broadcast and mine spend_2 and spend_3")
|
|
|
|
wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_2['hex'])
|
|
|
|
wallet.sendrawtransaction(from_node=self.nodes[0], tx_hex=spend_3['hex'])
|
|
|
|
self.log.info("Generate a block")
|
2024-10-01 21:25:52 +02:00
|
|
|
self.generate(self.nodes[0], 1)
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Check that time-locked transaction is still too immature to spend")
|
2018-08-13 13:29:32 +02:00
|
|
|
assert_raises_rpc_error(-26, 'non-final', self.nodes[0].sendrawtransaction, timelock_tx)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Create spend_2_1 and spend_3_1")
|
|
|
|
spend_2_utxo = wallet.get_utxo(txid=spend_2['txid'])
|
|
|
|
spend_2_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_2_utxo)
|
|
|
|
spend_3_utxo = wallet.get_utxo(txid=spend_3['txid'])
|
|
|
|
spend_3_1 = wallet.create_self_transfer(from_node=self.nodes[0], utxo_to_spend=spend_3_utxo)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Broadcast and mine spend_3_1")
|
|
|
|
spend_3_1_id = self.nodes[0].sendrawtransaction(spend_3_1['hex'])
|
|
|
|
self.log.info("Generate a block")
|
2024-10-01 21:25:52 +02:00
|
|
|
last_block = self.generate(self.nodes[0], 1)
|
2024-09-26 21:17:04 +02:00
|
|
|
# generate() implicitly syncs blocks, so that peer 1 gets the block before timelock_tx
|
2021-07-28 16:31:35 +02:00
|
|
|
# Otherwise, peer 1 would put the timelock_tx in m_recent_rejects
|
2020-04-24 13:57:41 +02:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("The time-locked transaction can now be spent")
|
2015-08-27 03:15:04 +02:00
|
|
|
timelock_tx_id = self.nodes[0].sendrawtransaction(timelock_tx)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Add spend_1 and spend_2_1 to the mempool")
|
|
|
|
spend_1_id = self.nodes[0].sendrawtransaction(spend_1['hex'])
|
|
|
|
spend_2_1_id = self.nodes[0].sendrawtransaction(spend_2_1['hex'])
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, timelock_tx_id})
|
2020-04-21 16:57:52 +02:00
|
|
|
self.sync_all()
|
2015-08-27 03:15:04 +02:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("invalidate the last block")
|
2015-08-27 03:15:04 +02:00
|
|
|
for node in self.nodes:
|
|
|
|
node.invalidateblock(last_block[0])
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("The time-locked transaction is now too immature and has been removed from the mempool")
|
|
|
|
self.log.info("spend_3_1 has been re-orged out of the chain and is back in the mempool")
|
|
|
|
assert_equal(set(self.nodes[0].getrawmempool()), {spend_1_id, spend_2_1_id, spend_3_1_id})
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Use invalidateblock to re-org back and make all those coinbase spends immature/invalid")
|
|
|
|
b = self.nodes[0].getblockhash(first_block + 100)
|
2014-11-24 21:18:05 +01:00
|
|
|
for node in self.nodes:
|
2021-06-01 15:04:55 +02:00
|
|
|
node.invalidateblock(b)
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2021-06-01 15:04:55 +02:00
|
|
|
self.log.info("Check that the mempool is empty")
|
2014-11-24 21:18:05 +01:00
|
|
|
assert_equal(set(self.nodes[0].getrawmempool()), set())
|
2020-04-21 16:57:52 +02:00
|
|
|
self.sync_all()
|
2014-11-24 21:18:05 +01:00
|
|
|
|
2020-03-15 15:14:34 +01:00
|
|
|
|
2014-11-24 21:18:05 +01:00
|
|
|
if __name__ == '__main__':
|
|
|
|
MempoolCoinbaseTest().main()
|