aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.md35
-rwxr-xr-xtest/functional/feature_addrman.py20
-rwxr-xr-xtest/functional/feature_backwards_compatibility.py283
-rwxr-xr-xtest/functional/feature_bind_port_discover.py78
-rwxr-xr-xtest/functional/feature_bind_port_externalip.py75
-rwxr-xr-xtest/functional/feature_blockfilterindex_prune.py10
-rwxr-xr-xtest/functional/feature_coinstatsindex.py60
-rwxr-xr-xtest/functional/feature_dirsymlinks.py24
-rwxr-xr-xtest/functional/feature_maxuploadtarget.py26
-rwxr-xr-xtest/functional/feature_proxy.py64
-rwxr-xr-xtest/functional/feature_pruning.py4
-rwxr-xr-xtest/functional/feature_segwit.py40
-rwxr-xr-xtest/functional/feature_taproot.py10
-rwxr-xr-xtest/functional/feature_txindex_compatibility.py2
-rwxr-xr-xtest/functional/feature_unsupported_utxo_db.py61
-rwxr-xr-xtest/functional/feature_utxo_set_hash.py4
-rwxr-xr-xtest/functional/interface_rest.py45
-rwxr-xr-xtest/functional/interface_zmq.py278
-rwxr-xr-xtest/functional/mempool_package_onemore.py58
-rwxr-xr-xtest/functional/mempool_unbroadcast.py45
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py83
-rwxr-xr-xtest/functional/p2p_blockfilters.py6
-rwxr-xr-xtest/functional/p2p_blocksonly.py2
-rwxr-xr-xtest/functional/p2p_message_capture.py2
-rwxr-xr-xtest/functional/rpc_blockchain.py13
-rwxr-xr-xtest/functional/rpc_createmultisig.py103
-rwxr-xr-xtest/functional/rpc_decodescript.py2
-rwxr-xr-xtest/functional/rpc_dumptxoutset.py6
-rwxr-xr-xtest/functional/rpc_generate.py91
-rwxr-xr-xtest/functional/rpc_generateblock.py100
-rwxr-xr-xtest/functional/rpc_help.py2
-rwxr-xr-xtest/functional/rpc_uptime.py2
-rw-r--r--test/functional/test_framework/script.py1
-rwxr-xr-xtest/functional/test_framework/test_framework.py11
-rwxr-xr-xtest/functional/test_framework/test_node.py3
-rw-r--r--test/functional/test_framework/util.py16
-rw-r--r--test/functional/test_framework/wallet.py63
-rwxr-xr-xtest/functional/test_runner.py19
-rwxr-xr-xtest/functional/wallet_balance.py4
-rwxr-xr-xtest/functional/wallet_basic.py8
-rwxr-xr-xtest/functional/wallet_bumpfee.py4
-rwxr-xr-xtest/functional/wallet_createwallet.py5
-rwxr-xr-xtest/functional/wallet_disable.py6
-rwxr-xr-xtest/functional/wallet_encryption.py6
-rwxr-xr-xtest/functional/wallet_importprunedfunds.py20
-rwxr-xr-xtest/functional/wallet_inactive_hdchains.py147
-rwxr-xr-xtest/functional/wallet_resendwallettransactions.py2
-rwxr-xr-xtest/functional/wallet_sendall.py316
-rwxr-xr-xtest/functional/wallet_signer.py6
-rwxr-xr-xtest/functional/wallet_taproot.py108
-rwxr-xr-xtest/functional/wallet_timelock.py2
-rwxr-xr-xtest/functional/wallet_upgradewallet.py11
-rwxr-xr-xtest/get_previous_releases.py26
-rwxr-xr-xtest/lint/lint-all.sh4
-rwxr-xr-xtest/lint/lint-files.py3
-rwxr-xr-xtest/lint/lint-files.sh10
-rwxr-xr-xtest/lint/lint-format-strings.sh4
-rwxr-xr-xtest/lint/lint-python-dead-code.py41
-rwxr-xr-xtest/lint/lint-python-dead-code.sh22
-rwxr-xr-xtest/lint/lint-spelling.py40
-rwxr-xr-xtest/lint/lint-spelling.sh21
-rwxr-xr-xtest/lint/run-lint-format-strings.py (renamed from test/lint/lint-format-strings.py)4
-rw-r--r--test/lint/spelling.ignore-words.txt (renamed from test/lint/lint-spelling.ignore-words.txt)0
-rw-r--r--test/sanitizer_suppressions/tsan5
-rw-r--r--test/sanitizer_suppressions/ubsan5
-rwxr-xr-xtest/util/test_runner.py2
66 files changed, 1755 insertions, 824 deletions
diff --git a/test/README.md b/test/README.md
index c9e15c4968..7ff2d6d9f2 100644
--- a/test/README.md
+++ b/test/README.md
@@ -98,7 +98,7 @@ test/functional/test_runner.py --extended
In order to run backwards compatibility tests, download the previous node binaries:
```
-test/get_previous_releases.py -b v0.20.1 v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2
+test/get_previous_releases.py -b v22.0 v0.21.0 v0.20.1 v0.19.1 v0.18.1 v0.17.2 v0.16.3 v0.15.2 v0.14.3
```
By default, up to 4 tests will be run in parallel by test_runner. To specify
@@ -107,6 +107,34 @@ how many jobs to run, append `--jobs=n`
The individual tests and the test_runner harness have many command-line
options. Run `test/functional/test_runner.py -h` to see them all.
+#### Speed up test runs with a ramdisk
+
+If you have available RAM on your system you can create a ramdisk to use as the `cache` and `tmp` directories for the functional tests in order to speed them up.
+Speed-up amount varies on each system (and according to your ram speed and other variables), but a 2-3x speed-up is not uncommon.
+
+To create a 4GB ramdisk on Linux at `/mnt/tmp/`:
+
+```bash
+sudo mkdir -p /mnt/tmp
+sudo mount -t tmpfs -o size=4g tmpfs /mnt/tmp/
+```
+
+Configure the size of the ramdisk using the `size=` option.
+The size of the ramdisk needed is relative to the number of concurrent jobs the test suite runs.
+For example running the test suite with `--jobs=100` might need a 4GB ramdisk, but running with `--jobs=32` will only need a 2.5GB ramdisk.
+
+To use, run the test suite specifying the ramdisk as the `cachedir` and `tmpdir`:
+
+```bash
+test/functional/test_runner.py --cachedir=/mnt/tmp/cache --tmpdir=/mnt/tmp
+```
+
+Once finished with the tests and the disk, and to free the ram, simply unmount the disk:
+
+```bash
+sudo umount /mnt/tmp
+```
+
#### Troubleshooting and debugging test failures
##### Resource contention
@@ -280,8 +308,9 @@ Use the `-v` option for verbose output.
| [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8)
| [`lint-python.sh`](lint/lint-python.sh) | [mypy](https://github.com/python/mypy)
| [`lint-python.sh`](lint/lint-python.sh) | [pyzmq](https://github.com/zeromq/pyzmq)
+| [`lint-python-dead-code.py`](lint/lint-python-dead-code.py) | [vulture](https://github.com/jendrikseipp/vulture)
| [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck)
-| [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell)
+| [`lint-spelling.py`](lint/lint-spelling.py) | [codespell](https://github.com/codespell-project/codespell)
In use versions and install instructions are available in the [CI setup](../ci/lint/04_install.sh).
@@ -292,7 +321,7 @@ Please be aware that on Linux distributions all dependencies are usually availab
Individual tests can be run by directly calling the test script, e.g.:
```
-test/lint/lint-files.sh
+test/lint/lint-files.py
```
You can run all the shell-based lint tests by running:
diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py
index 14a4f8abb7..5e49d0214a 100755
--- a/test/functional/feature_addrman.py
+++ b/test/functional/feature_addrman.py
@@ -68,18 +68,28 @@ class AddrmanTest(BitcoinTestFramework):
self.start_node(0, extra_args=["-checkaddrman=1"])
assert_equal(self.nodes[0].getnodeaddresses(), [])
- self.log.info("Check that addrman from future cannot be read")
+ self.log.info("Check that addrman with negative lowest_compatible cannot be read")
self.stop_node(0)
- write_addrman(peers_dat, lowest_compatible=111)
+ write_addrman(peers_dat, lowest_compatible=-32)
self.nodes[0].assert_start_raises_init_error(
expected_msg=init_error(
- "Unsupported format of addrman database: 1. It is compatible with "
- "formats >=111, but the maximum supported by this version of "
- f"{self.config['environment']['PACKAGE_NAME']} is 4.: (.+)"
+ "Corrupted addrman database: The compat value \\(0\\) is lower "
+ "than the expected minimum value 32.: (.+)"
),
match=ErrorMatch.FULL_REGEX,
)
+ self.log.info("Check that addrman from future is overwritten with new addrman")
+ self.stop_node(0)
+ write_addrman(peers_dat, lowest_compatible=111)
+ assert_equal(os.path.exists(peers_dat + ".bak"), False)
+ with self.nodes[0].assert_debug_log([
+ f'Creating new peers.dat because the file version was not compatible ("{peers_dat}"). Original backed up to peers.dat.bak',
+ ]):
+ self.start_node(0)
+ assert_equal(self.nodes[0].getnodeaddresses(), [])
+ assert_equal(os.path.exists(peers_dat + ".bak"), True)
+
self.log.info("Check that corrupt addrman cannot be read (EOF)")
self.stop_node(0)
with open(peers_dat, "wb") as f:
diff --git a/test/functional/feature_backwards_compatibility.py b/test/functional/feature_backwards_compatibility.py
index 476a6a0c14..a7fb3184a6 100755
--- a/test/functional/feature_backwards_compatibility.py
+++ b/test/functional/feature_backwards_compatibility.py
@@ -34,15 +34,18 @@ from test_framework.util import (
class BackwardsCompatibilityTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
- self.num_nodes = 6
+ self.num_nodes = 9
# Add new version after each release:
self.extra_args = [
- ["-addresstype=bech32"], # Pre-release: use to mine blocks
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # Pre-release: use to receive coins, swap wallets, etc
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.19.1
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.18.1
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32"], # v0.17.2
- ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-wallet=wallet.dat"], # v0.16.3
+ ["-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to mine blocks. noban for immediate tx relay
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # Pre-release: use to receive coins, swap wallets, etc
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v22.0
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.21.0
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.20.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=noban@127.0.0.1"], # v0.19.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=127.0.0.1"], # v0.18.1
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=127.0.0.1"], # v0.17.2
+ ["-nowallet", "-walletrbf=1", "-addresstype=bech32", "-whitelist=127.0.0.1", "-wallet=wallet.dat"], # v0.16.3
]
self.wallet_names = [self.default_wallet_name]
@@ -54,6 +57,9 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
None,
None,
+ 220000,
+ 210000,
+ 200100,
190100,
180100,
170200,
@@ -63,19 +69,27 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
self.start_nodes()
self.import_deterministic_coinbase_privkeys()
- def run_test(self):
- self.generatetoaddress(self.nodes[0], COINBASE_MATURITY + 1, self.nodes[0].getnewaddress())
-
- # Sanity check the test framework:
- res = self.nodes[self.num_nodes - 1].getblockchaininfo()
- assert_equal(res['blocks'], COINBASE_MATURITY + 1)
+ def nodes_wallet_dir(self, node):
+ if node.version < 170000:
+ return os.path.join(node.datadir, "regtest")
+ return os.path.join(node.datadir, "regtest/wallets")
- node_master = self.nodes[self.num_nodes - 5]
+ def run_test(self):
+ node_miner = self.nodes[0]
+ node_master = self.nodes[1]
node_v19 = self.nodes[self.num_nodes - 4]
node_v18 = self.nodes[self.num_nodes - 3]
node_v17 = self.nodes[self.num_nodes - 2]
node_v16 = self.nodes[self.num_nodes - 1]
+ legacy_nodes = self.nodes[2:]
+
+ self.generatetoaddress(node_miner, COINBASE_MATURITY + 1, node_miner.getnewaddress())
+
+ # Sanity check the test framework:
+ res = node_v16.getblockchaininfo()
+ assert_equal(res['blocks'], COINBASE_MATURITY + 1)
+
self.log.info("Test wallet backwards compatibility...")
# Create a number of wallets and open them in older versions:
@@ -88,21 +102,21 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
assert info['keypoolsize'] > 0
# Create a confirmed transaction, receiving coins
address = wallet.getnewaddress()
- self.nodes[0].sendtoaddress(address, 10)
+ node_miner.sendtoaddress(address, 10)
self.sync_mempools()
- self.generate(self.nodes[0], 1)
+ self.generate(node_miner, 1)
# Create a conflicting transaction using RBF
- return_address = self.nodes[0].getnewaddress()
- tx1_id = self.nodes[1].sendtoaddress(return_address, 1)
- tx2_id = self.nodes[1].bumpfee(tx1_id)["txid"]
+ return_address = node_miner.getnewaddress()
+ tx1_id = node_master.sendtoaddress(return_address, 1)
+ tx2_id = node_master.bumpfee(tx1_id)["txid"]
# Confirm the transaction
self.sync_mempools()
- self.generate(self.nodes[0], 1)
+ self.generate(node_miner, 1)
# Create another conflicting transaction using RBF
- tx3_id = self.nodes[1].sendtoaddress(return_address, 1)
- tx4_id = self.nodes[1].bumpfee(tx3_id)["txid"]
+ tx3_id = node_master.sendtoaddress(return_address, 1)
+ tx4_id = node_master.bumpfee(tx3_id)["txid"]
# Abandon transaction, but don't confirm
- self.nodes[1].abandontransaction(tx3_id)
+ node_master.abandontransaction(tx3_id)
# w1_v19: regular wallet, created with v0.19
node_v19.rpc.createwallet(wallet_name="w1_v19")
@@ -113,6 +127,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
# Use addmultisigaddress (see #18075)
address_18075 = wallet.rpc.addmultisigaddress(1, ["0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52", "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073"], "", "legacy")["address"]
assert wallet.getaddressinfo(address_18075)["solvable"]
+ node_v19.unloadwallet("w1_v19")
# w1_v18: regular wallet, created with v0.18
node_v18.rpc.createwallet(wallet_name="w1_v18")
@@ -130,20 +145,6 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
assert info['private_keys_enabled'] == False
assert info['keypoolsize'] == 0
- # w2_v19: wallet with private keys disabled, created with v0.19
- node_v19.rpc.createwallet(wallet_name="w2_v19", disable_private_keys=True)
- wallet = node_v19.get_wallet_rpc("w2_v19")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
-
- # w2_v18: wallet with private keys disabled, created with v0.18
- node_v18.rpc.createwallet(wallet_name="w2_v18", disable_private_keys=True)
- wallet = node_v18.get_wallet_rpc("w2_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
-
# w3: blank wallet, created on master: update this
# test when default blank wallets can no longer be opened by older versions.
node_master.createwallet(wallet_name="w3", blank=True)
@@ -152,170 +153,72 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
assert info['private_keys_enabled']
assert info['keypoolsize'] == 0
- # w3_v19: blank wallet, created with v0.19
- node_v19.rpc.createwallet(wallet_name="w3_v19", blank=True)
- wallet = node_v19.get_wallet_rpc("w3_v19")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] == 0
-
- # w3_v18: blank wallet, created with v0.18
- node_v18.rpc.createwallet(wallet_name="w3_v18", blank=True)
- wallet = node_v18.get_wallet_rpc("w3_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] == 0
-
- # Copy the wallets to older nodes:
+ # Unload wallets and copy to older nodes:
node_master_wallets_dir = os.path.join(node_master.datadir, "regtest/wallets")
node_v19_wallets_dir = os.path.join(node_v19.datadir, "regtest/wallets")
- node_v18_wallets_dir = os.path.join(node_v18.datadir, "regtest/wallets")
node_v17_wallets_dir = os.path.join(node_v17.datadir, "regtest/wallets")
node_v16_wallets_dir = os.path.join(node_v16.datadir, "regtest")
node_master.unloadwallet("w1")
node_master.unloadwallet("w2")
- node_v19.unloadwallet("w1_v19")
- node_v19.unloadwallet("w2_v19")
- node_v18.unloadwallet("w1_v18")
- node_v18.unloadwallet("w2_v18")
-
- # Copy wallets to v0.16
- for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(node_v16_wallets_dir, wallet)
- )
+ node_master.unloadwallet("w3")
- # Copy wallets to v0.17
- for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(node_v17_wallets_dir, wallet)
- )
- for wallet in os.listdir(node_v18_wallets_dir):
- shutil.copytree(
- os.path.join(node_v18_wallets_dir, wallet),
- os.path.join(node_v17_wallets_dir, wallet)
- )
-
- # Copy wallets to v0.18
- for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(node_v18_wallets_dir, wallet)
- )
-
- # Copy wallets to v0.19
- for wallet in os.listdir(node_master_wallets_dir):
- shutil.copytree(
- os.path.join(node_master_wallets_dir, wallet),
- os.path.join(node_v19_wallets_dir, wallet)
- )
+ for node in legacy_nodes:
+ # Copy wallets to previous version
+ for wallet in os.listdir(node_master_wallets_dir):
+ shutil.copytree(
+ os.path.join(node_master_wallets_dir, wallet),
+ os.path.join(self.nodes_wallet_dir(node), wallet)
+ )
if not self.options.descriptors:
# Descriptor wallets break compatibility, only run this test for legacy wallet
- # Open the wallets in v0.19
- node_v19.loadwallet("w1")
- wallet = node_v19.get_wallet_rpc("w1")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
- txs = wallet.listtransactions()
- assert_equal(len(txs), 5)
- assert_equal(txs[1]["txid"], tx1_id)
- assert_equal(txs[2]["walletconflicts"], [tx1_id])
- assert_equal(txs[1]["replaced_by_txid"], tx2_id)
- assert not(txs[1]["abandoned"])
- assert_equal(txs[1]["confirmations"], -1)
- assert_equal(txs[2]["blockindex"], 1)
- assert txs[3]["abandoned"]
- assert_equal(txs[4]["walletconflicts"], [tx3_id])
- assert_equal(txs[3]["replaced_by_txid"], tx4_id)
- assert not(hasattr(txs[3], "blockindex"))
-
- node_v19.loadwallet("w2")
- wallet = node_v19.get_wallet_rpc("w2")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
-
- node_v19.loadwallet("w3")
- wallet = node_v19.get_wallet_rpc("w3")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] == 0
-
- # Open the wallets in v0.18
- node_v18.loadwallet("w1")
- wallet = node_v18.get_wallet_rpc("w1")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
- txs = wallet.listtransactions()
- assert_equal(len(txs), 5)
- assert_equal(txs[1]["txid"], tx1_id)
- assert_equal(txs[2]["walletconflicts"], [tx1_id])
- assert_equal(txs[1]["replaced_by_txid"], tx2_id)
- assert not(txs[1]["abandoned"])
- assert_equal(txs[1]["confirmations"], -1)
- assert_equal(txs[2]["blockindex"], 1)
- assert txs[3]["abandoned"]
- assert_equal(txs[4]["walletconflicts"], [tx3_id])
- assert_equal(txs[3]["replaced_by_txid"], tx4_id)
- assert not(hasattr(txs[3], "blockindex"))
-
- node_v18.loadwallet("w2")
- wallet = node_v18.get_wallet_rpc("w2")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
-
- node_v18.loadwallet("w3")
- wallet = node_v18.get_wallet_rpc("w3")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] == 0
-
- node_v17.loadwallet("w1")
- wallet = node_v17.get_wallet_rpc("w1")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
-
- node_v17.loadwallet("w2")
- wallet = node_v17.get_wallet_rpc("w2")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
+ # Load modern wallet with older nodes
+ for node in legacy_nodes:
+ for wallet_name in ["w1", "w2", "w3"]:
+ if node.version < 170000:
+ # loadwallet was introduced in v0.17.0
+ continue
+ if node.version < 180000 and wallet_name == "w3":
+ # Blank wallets were introduced in v0.18.0. We test the loading error below.
+ continue
+ node.loadwallet(wallet_name)
+ wallet = node.get_wallet_rpc(wallet_name)
+ info = wallet.getwalletinfo()
+ if wallet_name == "w1":
+ assert info['private_keys_enabled'] == True
+ assert info['keypoolsize'] > 0
+ txs = wallet.listtransactions()
+ assert_equal(len(txs), 5)
+ assert_equal(txs[1]["txid"], tx1_id)
+ assert_equal(txs[2]["walletconflicts"], [tx1_id])
+ assert_equal(txs[1]["replaced_by_txid"], tx2_id)
+ assert not(txs[1]["abandoned"])
+ assert_equal(txs[1]["confirmations"], -1)
+ assert_equal(txs[2]["blockindex"], 1)
+ assert txs[3]["abandoned"]
+ assert_equal(txs[4]["walletconflicts"], [tx3_id])
+ assert_equal(txs[3]["replaced_by_txid"], tx4_id)
+ assert not(hasattr(txs[3], "blockindex"))
+ elif wallet_name == "w2":
+ assert(info['private_keys_enabled'] == False)
+ assert info['keypoolsize'] == 0
+ else:
+ assert(info['private_keys_enabled'] == True)
+ assert info['keypoolsize'] == 0
else:
- # Descriptor wallets appear to be corrupted wallets to old software
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v19.loadwallet, "w1")
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v19.loadwallet, "w2")
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v19.loadwallet, "w3")
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v18.loadwallet, "w1")
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v18.loadwallet, "w2")
- assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node_v18.loadwallet, "w3")
-
- # Open the wallets in v0.17
- node_v17.loadwallet("w1_v18")
- wallet = node_v17.get_wallet_rpc("w1_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled']
- assert info['keypoolsize'] > 0
-
- node_v17.loadwallet("w2_v18")
- wallet = node_v17.get_wallet_rpc("w2_v18")
- info = wallet.getwalletinfo()
- assert info['private_keys_enabled'] == False
- assert info['keypoolsize'] == 0
+ for node in legacy_nodes:
+ # Descriptor wallets appear to be corrupted wallets to old software
+ # and loadwallet is introduced in v0.17.0
+ if node.version >= 170000 and node.version < 210000:
+ for wallet_name in ["w1", "w2", "w3"]:
+ assert_raises_rpc_error(-4, "Wallet file verification failed: wallet.dat corrupt, salvage failed", node.loadwallet, wallet_name)
# RPC loadwallet failure causes bitcoind to exit, in addition to the RPC
# call failure, so the following test won't work:
- # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3_v18')
+ # assert_raises_rpc_error(-4, "Wallet loading failed.", node_v17.loadwallet, 'w3')
# Instead, we stop node and try to launch it with the wallet:
- self.stop_node(4)
- node_v17.assert_start_raises_init_error(["-wallet=w3_v18"], "Error: Error loading w3_v18: Wallet requires newer version of Bitcoin Core")
+ self.stop_node(node_v17.index)
if self.options.descriptors:
# Descriptor wallets appear to be corrupted wallets to old software
node_v17.assert_start_raises_init_error(["-wallet=w1"], "Error: wallet.dat corrupt, salvage failed")
@@ -323,23 +226,23 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: wallet.dat corrupt, salvage failed")
else:
node_v17.assert_start_raises_init_error(["-wallet=w3"], "Error: Error loading w3: Wallet requires newer version of Bitcoin Core")
- self.start_node(4)
+ self.start_node(node_v17.index)
if not self.options.descriptors:
# Descriptor wallets break compatibility, only run this test for legacy wallets
# Open most recent wallet in v0.16 (no loadwallet RPC)
- self.restart_node(5, extra_args=["-wallet=w2"])
+ self.restart_node(node_v16.index, extra_args=["-wallet=w2"])
wallet = node_v16.get_wallet_rpc("w2")
info = wallet.getwalletinfo()
assert info['keypoolsize'] == 1
# Create upgrade wallet in v0.16
- self.restart_node(-1, extra_args=["-wallet=u1_v16"])
+ self.restart_node(node_v16.index, extra_args=["-wallet=u1_v16"])
wallet = node_v16.get_wallet_rpc("u1_v16")
v16_addr = wallet.getnewaddress('', "bech32")
v16_info = wallet.validateaddress(v16_addr)
v16_pubkey = v16_info['pubkey']
- self.stop_node(-1)
+ self.stop_node(node_v16.index)
self.log.info("Test wallet upgrade path...")
# u1: regular wallet, created with v0.17
@@ -371,7 +274,7 @@ class BackwardsCompatibilityTest(BitcoinTestFramework):
os.path.join(node_master_wallets_dir, "u1_v16"),
os.path.join(node_v16_wallets_dir, "wallets/u1_v16")
)
- self.start_node(-1, extra_args=["-wallet=u1_v16"])
+ self.start_node(node_v16.index, extra_args=["-wallet=u1_v16"])
wallet = node_v16.get_wallet_rpc("u1_v16")
info = wallet.validateaddress(v16_addr)
assert_equal(info, v16_info)
diff --git a/test/functional/feature_bind_port_discover.py b/test/functional/feature_bind_port_discover.py
new file mode 100755
index 0000000000..6e07f2f16c
--- /dev/null
+++ b/test/functional/feature_bind_port_discover.py
@@ -0,0 +1,78 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020-2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""
+Test that -discover does not add all interfaces' addresses if we listen on only some of them
+"""
+
+from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.util import assert_equal
+
+# We need to bind to a routable address for this test to exercise the relevant code
+# and also must have another routable address on another interface which must not
+# be named "lo" or "lo0".
+# To set these routable addresses on the machine, use:
+# Linux:
+# ifconfig lo:0 1.1.1.1/32 up && ifconfig lo:1 2.2.2.2/32 up # to set up
+# ifconfig lo:0 down && ifconfig lo:1 down # to remove it, after the test
+# FreeBSD:
+# ifconfig em0 1.1.1.1/32 alias && ifconfig wlan0 2.2.2.2/32 alias # to set up
+# ifconfig em0 1.1.1.1 -alias && ifconfig wlan0 2.2.2.2 -alias # to remove it, after the test
+ADDR1 = '1.1.1.1'
+ADDR2 = '2.2.2.2'
+
+BIND_PORT = 31001
+
+class BindPortDiscoverTest(BitcoinTestFramework):
+ def set_test_params(self):
+ # Avoid any -bind= on the command line. Force the framework to avoid adding -bind=127.0.0.1.
+ self.setup_clean_chain = True
+ self.bind_to_localhost_only = False
+ self.extra_args = [
+ ['-discover', f'-port={BIND_PORT}'], # bind on any
+ ['-discover', f'-bind={ADDR1}:{BIND_PORT}'],
+ ]
+ self.num_nodes = len(self.extra_args)
+
+ def add_options(self, parser):
+ parser.add_argument(
+ "--ihave1111and2222", action='store_true', dest="ihave1111and2222",
+ help=f"Run the test, assuming {ADDR1} and {ADDR2} are configured on the machine",
+ default=False)
+
+ def skip_test_if_missing_module(self):
+ if not self.options.ihave1111and2222:
+ raise SkipTest(
+ f"To run this test make sure that {ADDR1} and {ADDR2} (routable addresses) are "
+ "assigned to the interfaces on this machine and rerun with --ihave1111and2222")
+
+ def run_test(self):
+ self.log.info(
+ "Test that if -bind= is not passed then all addresses are "
+ "added to localaddresses")
+ found_addr1 = False
+ found_addr2 = False
+ for local in self.nodes[0].getnetworkinfo()['localaddresses']:
+ if local['address'] == ADDR1:
+ found_addr1 = True
+ assert_equal(local['port'], BIND_PORT)
+ if local['address'] == ADDR2:
+ found_addr2 = True
+ assert_equal(local['port'], BIND_PORT)
+ assert found_addr1
+ assert found_addr2
+
+ self.log.info(
+ "Test that if -bind= is passed then only that address is "
+ "added to localaddresses")
+ found_addr1 = False
+ for local in self.nodes[1].getnetworkinfo()['localaddresses']:
+ if local['address'] == ADDR1:
+ found_addr1 = True
+ assert_equal(local['port'], BIND_PORT)
+ assert local['address'] != ADDR2
+ assert found_addr1
+
+if __name__ == '__main__':
+ BindPortDiscoverTest().main()
diff --git a/test/functional/feature_bind_port_externalip.py b/test/functional/feature_bind_port_externalip.py
new file mode 100755
index 0000000000..6a74ce5738
--- /dev/null
+++ b/test/functional/feature_bind_port_externalip.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+# Copyright (c) 2020-2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""
+Test that the proper port is used for -externalip=
+"""
+
+from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.util import assert_equal, p2p_port
+
+# We need to bind to a routable address for this test to exercise the relevant code.
+# To set a routable address on the machine use:
+# Linux:
+# ifconfig lo:0 1.1.1.1/32 up # to set up
+# ifconfig lo:0 down # to remove it, after the test
+# FreeBSD:
+# ifconfig lo0 1.1.1.1/32 alias # to set up
+# ifconfig lo0 1.1.1.1 -alias # to remove it, after the test
+ADDR = '1.1.1.1'
+
+# array of tuples [arguments, expected port in localaddresses]
+EXPECTED = [
+ [['-externalip=2.2.2.2', '-port=30001'], 30001],
+ [['-externalip=2.2.2.2', '-port=30002', f'-bind={ADDR}'], 30002],
+ [['-externalip=2.2.2.2', f'-bind={ADDR}'], 'default_p2p_port'],
+ [['-externalip=2.2.2.2', '-port=30003', f'-bind={ADDR}:30004'], 30004],
+ [['-externalip=2.2.2.2', f'-bind={ADDR}:30005'], 30005],
+ [['-externalip=2.2.2.2:30006', '-port=30007'], 30006],
+ [['-externalip=2.2.2.2:30008', '-port=30009', f'-bind={ADDR}'], 30008],
+ [['-externalip=2.2.2.2:30010', f'-bind={ADDR}'], 30010],
+ [['-externalip=2.2.2.2:30011', '-port=30012', f'-bind={ADDR}:30013'], 30011],
+ [['-externalip=2.2.2.2:30014', f'-bind={ADDR}:30015'], 30014],
+ [['-externalip=2.2.2.2', '-port=30016', f'-bind={ADDR}:30017',
+ f'-whitebind={ADDR}:30018'], 30017],
+ [['-externalip=2.2.2.2', '-port=30019',
+ f'-whitebind={ADDR}:30020'], 30020],
+]
+
+class BindPortExternalIPTest(BitcoinTestFramework):
+ def set_test_params(self):
+ # Avoid any -bind= on the command line. Force the framework to avoid adding -bind=127.0.0.1.
+ self.setup_clean_chain = True
+ self.bind_to_localhost_only = False
+ self.num_nodes = len(EXPECTED)
+ self.extra_args = list(map(lambda e: e[0], EXPECTED))
+
+ def add_options(self, parser):
+ parser.add_argument(
+ "--ihave1111", action='store_true', dest="ihave1111",
+ help=f"Run the test, assuming {ADDR} is configured on the machine",
+ default=False)
+
+ def skip_test_if_missing_module(self):
+ if not self.options.ihave1111:
+ raise SkipTest(
+ f"To run this test make sure that {ADDR} (a routable address) is assigned "
+ "to one of the interfaces on this machine and rerun with --ihave1111")
+
+ def run_test(self):
+ self.log.info("Test the proper port is used for -externalip=")
+ for i in range(len(EXPECTED)):
+ expected_port = EXPECTED[i][1]
+ if expected_port == 'default_p2p_port':
+ expected_port = p2p_port(i)
+ found = False
+ for local in self.nodes[i].getnetworkinfo()['localaddresses']:
+ if local['address'] == '2.2.2.2':
+ assert_equal(local['port'], expected_port)
+ found = True
+ break
+ assert found
+
+if __name__ == '__main__':
+ BindPortExternalIPTest().main()
diff --git a/test/functional/feature_blockfilterindex_prune.py b/test/functional/feature_blockfilterindex_prune.py
index 2451988135..c983ceda6f 100755
--- a/test/functional/feature_blockfilterindex_prune.py
+++ b/test/functional/feature_blockfilterindex_prune.py
@@ -31,7 +31,7 @@ class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
pruneheight = self.nodes[0].pruneblockchain(400)
# the prune heights used here and below are magic numbers that are determined by the
# thresholds at which block files wrap, so they depend on disk serialization and default block file size.
- assert_equal(pruneheight, 248)
+ assert_equal(pruneheight, 249)
self.log.info("check if we can access the tips blockfilter when we have pruned some blocks")
assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getbestblockhash())['filter']), 0)
@@ -40,19 +40,19 @@ class FeatureBlockfilterindexPruneTest(BitcoinTestFramework):
assert_greater_than(len(self.nodes[0].getblockfilter(self.nodes[0].getblockhash(2))['filter']), 0)
# mine and sync index up to a height that will later be the pruneheight
- self.generate(self.nodes[0], 298)
- self.sync_index(height=998)
+ self.generate(self.nodes[0], 51)
+ self.sync_index(height=751)
self.log.info("start node without blockfilterindex")
self.restart_node(0, extra_args=["-fastprune", "-prune=1"])
self.log.info("make sure accessing the blockfilters throws an error")
assert_raises_rpc_error(-1, "Index is not enabled for filtertype basic", self.nodes[0].getblockfilter, self.nodes[0].getblockhash(2))
- self.generate(self.nodes[0], 502)
+ self.generate(self.nodes[0], 749)
self.log.info("prune exactly up to the blockfilterindexes best block while blockfilters are disabled")
pruneheight_2 = self.nodes[0].pruneblockchain(1000)
- assert_equal(pruneheight_2, 998)
+ assert_equal(pruneheight_2, 751)
self.restart_node(0, extra_args=["-fastprune", "-prune=1", "-blockfilterindex=1"])
self.log.info("make sure that we can continue with the partially synced index after having pruned up to the index height")
self.sync_index(height=1500)
diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py
index c70f8a83db..f865661894 100755
--- a/test/functional/feature_coinstatsindex.py
+++ b/test/functional/feature_coinstatsindex.py
@@ -18,9 +18,6 @@ from test_framework.blocktools import (
)
from test_framework.messages import (
COIN,
- COutPoint,
- CTransaction,
- CTxIn,
CTxOut,
)
from test_framework.script import (
@@ -33,6 +30,11 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
+from test_framework.wallet import (
+ MiniWallet,
+ getnewdestination,
+)
+
class CoinStatsIndexTest(BitcoinTestFramework):
def set_test_params(self):
@@ -40,16 +42,12 @@ class CoinStatsIndexTest(BitcoinTestFramework):
self.num_nodes = 2
self.supports_cli = False
self.extra_args = [
- # Explicitly set the output type in order to have consistent tx vsize / fees
- # for both legacy and descriptor wallets (disables the change address type detection algorithm)
- ["-addresstype=bech32", "-changetype=bech32"],
+ [],
["-coinstatsindex"]
]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
self._test_coin_stats_index()
self._test_use_index_option()
self._test_reorg_index()
@@ -69,9 +67,8 @@ class CoinStatsIndexTest(BitcoinTestFramework):
index_hash_options = ['none', 'muhash']
# Generate a normal transaction and mine it
- self.generate(node, COINBASE_MATURITY + 1)
- address = self.nodes[0].get_deterministic_priv_key().address
- node.sendtoaddress(address=address, amount=10, subtractfeefromamount=True)
+ self.generate(self.wallet, COINBASE_MATURITY + 1)
+ self.wallet.send_self_transfer(from_node=node)
self.generate(node, 1)
self.log.info("Test that gettxoutsetinfo() output is consistent with or without coinstatsindex option")
@@ -136,36 +133,31 @@ class CoinStatsIndexTest(BitcoinTestFramework):
assert_equal(res5['block_info'], {
'unspendable': 0,
'prevout_spent': 50,
- 'new_outputs_ex_coinbase': Decimal('49.99995560'),
- 'coinbase': Decimal('50.00004440'),
+ 'new_outputs_ex_coinbase': Decimal('49.99968800'),
+ 'coinbase': Decimal('50.00031200'),
'unspendables': {
'genesis_block': 0,
'bip30': 0,
'scripts': 0,
- 'unclaimed_rewards': 0
+ 'unclaimed_rewards': 0,
}
})
self.block_sanity_check(res5['block_info'])
# Generate and send a normal tx with two outputs
- tx1_inputs = []
- tx1_outputs = {self.nodes[0].getnewaddress(): 21, self.nodes[0].getnewaddress(): 42}
- raw_tx1 = self.nodes[0].createrawtransaction(tx1_inputs, tx1_outputs)
- funded_tx1 = self.nodes[0].fundrawtransaction(raw_tx1)
- signed_tx1 = self.nodes[0].signrawtransactionwithwallet(funded_tx1['hex'])
- tx1_txid = self.nodes[0].sendrawtransaction(signed_tx1['hex'])
+ tx1_txid, tx1_vout = self.wallet.send_to(
+ from_node=node,
+ scriptPubKey=self.wallet.get_scriptPubKey(),
+ amount=21 * COIN,
+ )
# Find the right position of the 21 BTC output
- tx1_final = self.nodes[0].gettransaction(tx1_txid)
- for output in tx1_final['details']:
- if output['amount'] == Decimal('21.00000000') and output['category'] == 'receive':
- n = output['vout']
+ tx1_out_21 = self.wallet.get_utxo(txid=tx1_txid, vout=tx1_vout)
# Generate and send another tx with an OP_RETURN output (which is unspendable)
- tx2 = CTransaction()
- tx2.vin.append(CTxIn(COutPoint(int(tx1_txid, 16), n), b''))
- tx2.vout.append(CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE]*30)))
- tx2_hex = self.nodes[0].signrawtransactionwithwallet(tx2.serialize().hex())['hex']
+ tx2 = self.wallet.create_self_transfer(utxo_to_spend=tx1_out_21)['tx']
+ tx2.vout = [CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
+ tx2_hex = tx2.serialize().hex()
self.nodes[0].sendrawtransaction(tx2_hex)
# Include both txs in a block
@@ -177,14 +169,14 @@ class CoinStatsIndexTest(BitcoinTestFramework):
assert_equal(res6['total_unspendable_amount'], Decimal('70.99000000'))
assert_equal(res6['block_info'], {
'unspendable': Decimal('20.99000000'),
- 'prevout_spent': 111,
- 'new_outputs_ex_coinbase': Decimal('89.99993620'),
- 'coinbase': Decimal('50.01006380'),
+ 'prevout_spent': 71,
+ 'new_outputs_ex_coinbase': Decimal('49.99999000'),
+ 'coinbase': Decimal('50.01001000'),
'unspendables': {
'genesis_block': 0,
'bip30': 0,
'scripts': Decimal('20.99000000'),
- 'unclaimed_rewards': 0
+ 'unclaimed_rewards': 0,
}
})
self.block_sanity_check(res6['block_info'])
@@ -246,7 +238,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
# Generate two block, let the index catch up, then invalidate the blocks
index_node = self.nodes[1]
- reorg_blocks = self.generatetoaddress(index_node, 2, index_node.getnewaddress())
+ reorg_blocks = self.generatetoaddress(index_node, 2, getnewdestination()[2])
reorg_block = reorg_blocks[1]
res_invalid = index_node.gettxoutsetinfo('muhash')
index_node.invalidateblock(reorg_blocks[0])
diff --git a/test/functional/feature_dirsymlinks.py b/test/functional/feature_dirsymlinks.py
index 85c8e27600..288754c04c 100755
--- a/test/functional/feature_dirsymlinks.py
+++ b/test/functional/feature_dirsymlinks.py
@@ -6,9 +6,8 @@
"""
import os
-import sys
-from test_framework.test_framework import BitcoinTestFramework, SkipTest
+from test_framework.test_framework import BitcoinTestFramework
def rename_and_link(*, from_name, to_name):
@@ -16,24 +15,27 @@ def rename_and_link(*, from_name, to_name):
os.symlink(to_name, from_name)
assert os.path.islink(from_name) and os.path.isdir(from_name)
-class SymlinkTest(BitcoinTestFramework):
- def skip_test_if_missing_module(self):
- if sys.platform == 'win32':
- raise SkipTest("Symlinks test skipped on Windows")
+class SymlinkTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
def run_test(self):
+ dir_new_blocks = self.nodes[0].chain_path / "new_blocks"
+ dir_new_chainstate = self.nodes[0].chain_path / "new_chainstate"
self.stop_node(0)
- rename_and_link(from_name=os.path.join(self.nodes[0].datadir, self.chain, "blocks"),
- to_name=os.path.join(self.nodes[0].datadir, self.chain, "newblocks"))
- rename_and_link(from_name=os.path.join(self.nodes[0].datadir, self.chain, "chainstate"),
- to_name=os.path.join(self.nodes[0].datadir, self.chain, "newchainstate"))
+ rename_and_link(
+ from_name=self.nodes[0].chain_path / "blocks",
+ to_name=dir_new_blocks,
+ )
+ rename_and_link(
+ from_name=self.nodes[0].chain_path / "chainstate",
+ to_name=dir_new_chainstate,
+ )
self.start_node(0)
-if __name__ == '__main__':
+if __name__ == "__main__":
SymlinkTest().main()
diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py
index 24f79dda67..0b9d651226 100755
--- a/test/functional/feature_maxuploadtarget.py
+++ b/test/functional/feature_maxuploadtarget.py
@@ -13,10 +13,19 @@ if uploadtarget has been reached.
from collections import defaultdict
import time
-from test_framework.messages import CInv, MSG_BLOCK, msg_getdata
+from test_framework.messages import (
+ CInv,
+ MSG_BLOCK,
+ msg_getdata,
+)
from test_framework.p2p import P2PInterface
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, mine_large_block
+from test_framework.util import (
+ assert_equal,
+ mine_large_block,
+)
+from test_framework.wallet import MiniWallet
+
class TestP2PConn(P2PInterface):
def __init__(self):
@@ -41,12 +50,6 @@ class MaxUploadTest(BitcoinTestFramework):
]]
self.supports_cli = False
- # Cache for utxos, as the listunspent may take a long time later in the test
- self.utxo_cache = []
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
-
def run_test(self):
# Before we connect anything, we first set the time on the node
# to be in the past, otherwise things break because the CNode
@@ -55,7 +58,8 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].setmocktime(old_time)
# Generate some old blocks
- self.generate(self.nodes[0], 130)
+ self.wallet = MiniWallet(self.nodes[0])
+ self.generate(self.wallet, 130)
# p2p_conns[0] will only request old blocks
# p2p_conns[1] will only request new blocks
@@ -66,7 +70,7 @@ class MaxUploadTest(BitcoinTestFramework):
p2p_conns.append(self.nodes[0].add_p2p_connection(TestP2PConn()))
# Now mine a big block
- mine_large_block(self, self.nodes[0], self.utxo_cache)
+ mine_large_block(self, self.wallet, self.nodes[0])
# Store the hash; we'll request this later
big_old_block = self.nodes[0].getbestblockhash()
@@ -77,7 +81,7 @@ class MaxUploadTest(BitcoinTestFramework):
self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24)
# Mine one more block, so that the prior block looks old
- mine_large_block(self, self.nodes[0], self.utxo_cache)
+ mine_large_block(self, self.wallet, self.nodes[0])
# We'll be requesting this new block too
big_new_block = self.nodes[0].getbestblockhash()
diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py
index 7d9e5b70fc..8541c3ed88 100755
--- a/test/functional/feature_proxy.py
+++ b/test/functional/feature_proxy.py
@@ -30,6 +30,12 @@ addnode connect to generic DNS name
addnode connect to a CJDNS address
- Test getnetworkinfo for each node
+
+- Test passing invalid -proxy
+- Test passing invalid -onion
+- Test passing invalid -i2psam
+- Test passing -onlynet=onion without -proxy or -onion
+- Test passing -onlynet=onion with -onion=0 and with -noonion
"""
import socket
@@ -234,7 +240,15 @@ class ProxyTest(BitcoinTestFramework):
return r
self.log.info("Test RPC getnetworkinfo")
- n0 = networks_dict(self.nodes[0].getnetworkinfo())
+ nodes_network_info = []
+
+ self.log.debug("Test that setting -proxy disables local address discovery, i.e. -discover=0")
+ for node in self.nodes:
+ network_info = node.getnetworkinfo()
+ assert_equal(network_info["localaddresses"], [])
+ nodes_network_info.append(network_info)
+
+ n0 = networks_dict(nodes_network_info[0])
assert_equal(NETWORKS, n0.keys())
for net in NETWORKS:
if net == NET_I2P:
@@ -249,7 +263,7 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n0['i2p']['reachable'], False)
assert_equal(n0['cjdns']['reachable'], False)
- n1 = networks_dict(self.nodes[1].getnetworkinfo())
+ n1 = networks_dict(nodes_network_info[1])
assert_equal(NETWORKS, n1.keys())
for net in ['ipv4', 'ipv6']:
assert_equal(n1[net]['proxy'], f'{self.conf1.addr[0]}:{self.conf1.addr[1]}')
@@ -261,14 +275,15 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n1['i2p']['proxy_randomize_credentials'], False)
assert_equal(n1['i2p']['reachable'], True)
- n2 = networks_dict(self.nodes[2].getnetworkinfo())
+ n2 = networks_dict(nodes_network_info[2])
assert_equal(NETWORKS, n2.keys())
+ proxy = f'{self.conf2.addr[0]}:{self.conf2.addr[1]}'
for net in NETWORKS:
if net == NET_I2P:
expected_proxy = ''
expected_randomize = False
else:
- expected_proxy = f'{self.conf2.addr[0]}:{self.conf2.addr[1]}'
+ expected_proxy = proxy
expected_randomize = True
assert_equal(n2[net]['proxy'], expected_proxy)
assert_equal(n2[net]['proxy_randomize_credentials'], expected_randomize)
@@ -277,20 +292,18 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n2['cjdns']['reachable'], False)
if self.have_ipv6:
- n3 = networks_dict(self.nodes[3].getnetworkinfo())
+ n3 = networks_dict(nodes_network_info[3])
assert_equal(NETWORKS, n3.keys())
+ proxy = f'[{self.conf3.addr[0]}]:{self.conf3.addr[1]}'
for net in NETWORKS:
- if net == NET_I2P:
- expected_proxy = ''
- else:
- expected_proxy = f'[{self.conf3.addr[0]}]:{self.conf3.addr[1]}'
+ expected_proxy = '' if net == NET_I2P or net == NET_ONION else proxy
assert_equal(n3[net]['proxy'], expected_proxy)
assert_equal(n3[net]['proxy_randomize_credentials'], False)
assert_equal(n3['onion']['reachable'], False)
assert_equal(n3['i2p']['reachable'], False)
assert_equal(n3['cjdns']['reachable'], False)
- n4 = networks_dict(self.nodes[4].getnetworkinfo())
+ n4 = networks_dict(nodes_network_info[4])
assert_equal(NETWORKS, n4.keys())
for net in NETWORKS:
if net == NET_I2P:
@@ -305,6 +318,37 @@ class ProxyTest(BitcoinTestFramework):
assert_equal(n4['i2p']['reachable'], False)
assert_equal(n4['cjdns']['reachable'], True)
+ self.stop_node(1)
+
+ self.log.info("Test passing invalid -proxy raises expected init error")
+ self.nodes[1].extra_args = ["-proxy=abc:def"]
+ msg = "Error: Invalid -proxy address or hostname: 'abc:def'"
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
+ self.log.info("Test passing invalid -onion raises expected init error")
+ self.nodes[1].extra_args = ["-onion=xyz:abc"]
+ msg = "Error: Invalid -onion address or hostname: 'xyz:abc'"
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
+ self.log.info("Test passing invalid -i2psam raises expected init error")
+ self.nodes[1].extra_args = ["-i2psam=def:xyz"]
+ msg = "Error: Invalid -i2psam address or hostname: 'def:xyz'"
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
+ msg = (
+ "Error: Outbound connections restricted to Tor (-onlynet=onion) but "
+ "the proxy for reaching the Tor network is not provided (no -proxy= "
+ "and no -onion= given) or it is explicitly forbidden (-onion=0)"
+ )
+ self.log.info("Test passing -onlynet=onion without -proxy or -onion raises expected init error")
+ self.nodes[1].extra_args = ["-onlynet=onion"]
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
+ self.log.info("Test passing -onlynet=onion with -onion=0/-noonion raises expected init error")
+ for arg in ["-onion=0", "-noonion"]:
+ self.nodes[1].extra_args = ["-onlynet=onion", arg]
+ self.nodes[1].assert_start_raises_init_error(expected_msg=msg)
+
if __name__ == '__main__':
ProxyTest().main()
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index ba3c5053cb..bf19384279 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -141,6 +141,10 @@ class PruneTest(BitcoinTestFramework):
expected_msg='Error: Prune mode is incompatible with -coinstatsindex.',
extra_args=['-prune=550', '-coinstatsindex'],
)
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg='Error: Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead.',
+ extra_args=['-prune=550', '-reindex-chainstate'],
+ )
def test_height_min(self):
assert os.path.isfile(os.path.join(self.prunedir, "blk00000.dat")), "blk00000.dat is missing, pruning too early"
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 6d7f1def88..f0faf1421b 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -86,18 +86,18 @@ class SegWitTest(BitcoinTestFramework):
[
"-acceptnonstdtxn=1",
"-rpcserialversion=0",
- "-testactivationheight=segwit@432",
+ "-testactivationheight=segwit@165",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
"-rpcserialversion=1",
- "-testactivationheight=segwit@432",
+ "-testactivationheight=segwit@165",
"-addresstype=legacy",
],
[
"-acceptnonstdtxn=1",
- "-testactivationheight=segwit@432",
+ "-testactivationheight=segwit@165",
"-addresstype=legacy",
],
]
@@ -117,12 +117,6 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(len(node.getblock(block[0])["tx"]), 2)
self.sync_blocks()
- def skip_mine(self, node, txid, sign, redeem_script=""):
- send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)
- block = self.generate(node, 1)
- assert_equal(len(node.getblock(block[0])["tx"]), 1)
- self.sync_blocks()
-
def fail_accept(self, node, error_msg, txid, sign, redeem_script=""):
assert_raises_rpc_error(-26, error_msg, send_to_witness, use_p2wsh=1, node=node, utxo=getutxo(txid), pubkey=self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=sign, insert_redeem_script=redeem_script)
@@ -197,23 +191,21 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999"))
assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999"))
- self.generate(self.nodes[0], 260) # block 423
-
- self.log.info("Verify witness txs are skipped for mining before the fork")
- self.skip_mine(self.nodes[2], wit_ids[NODE_2][P2WPKH][0], True) # block 424
- self.skip_mine(self.nodes[2], wit_ids[NODE_2][P2WSH][0], True) # block 425
- self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WPKH][0], True) # block 426
- self.skip_mine(self.nodes[2], p2sh_ids[NODE_2][P2WSH][0], True) # block 427
-
self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid")
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False)
self.fail_accept(self.nodes[2], "mandatory-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False)
- self.generate(self.nodes[2], 4) # blocks 428-431
+ self.generate(self.nodes[0], 1) # block 164
+
+ self.log.info("Verify witness txs are mined as soon as segwit activates")
+
+ send_to_witness(1, self.nodes[2], getutxo(wit_ids[NODE_2][P2WPKH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)
+ send_to_witness(1, self.nodes[2], getutxo(wit_ids[NODE_2][P2WSH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)
+ send_to_witness(1, self.nodes[2], getutxo(p2sh_ids[NODE_2][P2WPKH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)
+ send_to_witness(1, self.nodes[2], getutxo(p2sh_ids[NODE_2][P2WSH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)
- self.log.info("Verify previous witness txs skipped for mining can now be mined")
assert_equal(len(self.nodes[2].getrawmempool()), 4)
- blockhash = self.generate(self.nodes[2], 1)[0] # block 432 (first block with new rules; 432 = 144 * 3)
+ blockhash = self.generate(self.nodes[2], 1)[0] # block 165 (first block with new rules)
assert_equal(len(self.nodes[2].getrawmempool()), 0)
segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"]
assert_equal(len(segwit_tx_list), 5)
@@ -255,10 +247,10 @@ class SegWitTest(BitcoinTestFramework):
self.fail_accept(self.nodes[2], 'non-mandatory-script-verify-flag (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2]))
self.log.info("Verify default node can now use witness txs")
- self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True) # block 432
- self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WSH][0], True) # block 433
- self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WPKH][0], True) # block 434
- self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WSH][0], True) # block 435
+ self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True)
+ self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WSH][0], True)
+ self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WPKH][0], True)
+ self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WSH][0], True)
self.log.info("Verify sigops are counted in GBT with BIP141 rules after the fork")
txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)
diff --git a/test/functional/feature_taproot.py b/test/functional/feature_taproot.py
index 3e3d4b3c77..c3925dbb00 100755
--- a/test/functional/feature_taproot.py
+++ b/test/functional/feature_taproot.py
@@ -1182,15 +1182,11 @@ def spenders_taproot_inactive():
]
tap = taproot_construct(pub, scripts)
- # Test that keypath spending is valid & non-standard, regardless of validity.
+ # Test that valid spending is standard.
add_spender(spenders, "inactive/keypath_valid", key=sec, tap=tap, standard=Standard.V23)
- add_spender(spenders, "inactive/keypath_invalidsig", key=sec, tap=tap, standard=False, sighash=bitflipper(default_sighash))
- add_spender(spenders, "inactive/keypath_empty", key=sec, tap=tap, standard=False, witness=[])
-
- # Same for scriptpath spending (and features like annex, leaf versions, or OP_SUCCESS don't change this)
add_spender(spenders, "inactive/scriptpath_valid", key=sec, tap=tap, leaf="pk", standard=Standard.V23, inputs=[getter("sign")])
- add_spender(spenders, "inactive/scriptpath_invalidsig", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
- add_spender(spenders, "inactive/scriptpath_invalidcb", key=sec, tap=tap, leaf="pk", standard=False, inputs=[getter("sign")], controlblock=bitflipper(default_controlblock))
+
+ # Test that features like annex, leaf versions, or OP_SUCCESS are valid but non-standard
add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])
add_spender(spenders, "inactive/scriptpath_invalid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))
add_spender(spenders, "inactive/scriptpath_valid_opsuccess", key=sec, tap=tap, leaf="op_success", standard=False, inputs=[getter("sign")])
diff --git a/test/functional/feature_txindex_compatibility.py b/test/functional/feature_txindex_compatibility.py
index bbe1d1b537..20b023d82c 100755
--- a/test/functional/feature_txindex_compatibility.py
+++ b/test/functional/feature_txindex_compatibility.py
@@ -78,7 +78,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
self.stop_nodes()
- self.log.info("Check migrated txindex can not be read by legacy node")
+ self.log.info("Check migrated txindex cannot be read by legacy node")
err_msg = f": You need to rebuild the database using -reindex to change -txindex.{os.linesep}Please restart with -reindex or -reindex-chainstate to recover."
shutil.rmtree(legacy_chain_dir)
shutil.copytree(migrate_chain_dir, legacy_chain_dir)
diff --git a/test/functional/feature_unsupported_utxo_db.py b/test/functional/feature_unsupported_utxo_db.py
new file mode 100755
index 0000000000..1c8c08d1d8
--- /dev/null
+++ b/test/functional/feature_unsupported_utxo_db.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test that unsupported utxo db causes an init error.
+
+Previous releases are required by this test, see test/README.md.
+"""
+
+import shutil
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import assert_equal
+
+
+class UnsupportedUtxoDbTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_previous_releases()
+
+ def setup_network(self):
+ self.add_nodes(
+ self.num_nodes,
+ versions=[
+ 140300, # Last release with previous utxo db format
+ None, # For MiniWallet, without migration code
+ ],
+ )
+
+ def run_test(self):
+ self.log.info("Create previous version (v0.14.3) utxo db")
+ self.start_node(0)
+ block = self.generate(self.nodes[0], 1, sync_fun=self.no_op)[-1]
+ assert_equal(self.nodes[0].getbestblockhash(), block)
+ assert_equal(self.nodes[0].gettxoutsetinfo()["total_amount"], 50)
+ self.stop_nodes()
+
+ self.log.info("Check init error")
+ legacy_utxos_dir = self.nodes[0].chain_path / "chainstate"
+ legacy_blocks_dir = self.nodes[0].chain_path / "blocks"
+ recent_utxos_dir = self.nodes[1].chain_path / "chainstate"
+ recent_blocks_dir = self.nodes[1].chain_path / "blocks"
+ shutil.copytree(legacy_utxos_dir, recent_utxos_dir)
+ shutil.copytree(legacy_blocks_dir, recent_blocks_dir)
+ self.nodes[1].assert_start_raises_init_error(
+ expected_msg="Error: Unsupported chainstate database format found. "
+ "Please restart with -reindex-chainstate. "
+ "This will rebuild the chainstate database.",
+ )
+
+ self.log.info("Drop legacy utxo db")
+ self.start_node(1, extra_args=["-reindex-chainstate"])
+ assert_equal(self.nodes[1].getbestblockhash(), block)
+ assert_equal(self.nodes[1].gettxoutsetinfo()["total_amount"], 50)
+
+
+if __name__ == "__main__":
+ UnsupportedUtxoDbTest().main()
diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py
index 75180e62a2..4d486bc6f4 100755
--- a/test/functional/feature_utxo_set_hash.py
+++ b/test/functional/feature_utxo_set_hash.py
@@ -69,8 +69,8 @@ class UTXOSetHashTest(BitcoinTestFramework):
assert_equal(finalized[::-1].hex(), node_muhash)
self.log.info("Test deterministic UTXO set hash results")
- assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "3a570529b4c32e77268de1f81b903c75cc2da53c48df0d125c1e697ba7c8c7b7")
- assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "a13e0e70eb8acc786549596e3bc154623f1a5a622ba2f70715f6773ec745f435")
+ assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "f9aa4fb5ffd10489b9a6994e70ccf1de8a8bfa2d5f201d9857332e9954b0855d")
+ assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "d1725b2fe3ef43e55aa4907480aea98d406fc9e0bf8f60169e2305f1fbf5961b")
def run_test(self):
self.test_muhash_implementation()
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index a3d949c6a8..4f8676ec53 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -10,6 +10,7 @@ import http.client
from io import BytesIO
import json
from struct import pack, unpack
+import typing
import urllib.parse
@@ -57,14 +58,21 @@ class RESTTest (BitcoinTestFramework):
args.append("-whitelist=noban@127.0.0.1")
self.supports_cli = False
- def test_rest_request(self, uri, http_method='GET', req_type=ReqType.JSON, body='', status=200, ret_type=RetType.JSON):
+ def test_rest_request(
+ self,
+ uri: str,
+ http_method: str = 'GET',
+ req_type: ReqType = ReqType.JSON,
+ body: str = '',
+ status: int = 200,
+ ret_type: RetType = RetType.JSON,
+ query_params: typing.Dict[str, typing.Any] = None,
+ ) -> typing.Union[http.client.HTTPResponse, bytes, str, None]:
rest_uri = '/rest' + uri
- if req_type == ReqType.JSON:
- rest_uri += '.json'
- elif req_type == ReqType.BIN:
- rest_uri += '.bin'
- elif req_type == ReqType.HEX:
- rest_uri += '.hex'
+ if req_type in ReqType:
+ rest_uri += f'.{req_type.name.lower()}'
+ if query_params:
+ rest_uri += f'?{urllib.parse.urlencode(query_params)}'
conn = http.client.HTTPConnection(self.url.hostname, self.url.port)
self.log.debug(f'{http_method} {rest_uri} {body}')
@@ -83,6 +91,8 @@ class RESTTest (BitcoinTestFramework):
elif ret_type == RetType.JSON:
return json.loads(resp.read().decode('utf-8'), parse_float=Decimal)
+ return None
+
def run_test(self):
self.url = urllib.parse.urlparse(self.nodes[0].url)
self.wallet = MiniWallet(self.nodes[0])
@@ -213,12 +223,12 @@ class RESTTest (BitcoinTestFramework):
bb_hash = self.nodes[0].getbestblockhash()
# Check result if block does not exists
- assert_equal(self.test_rest_request(f"/headers/1/{UNKNOWN_PARAM}"), [])
+ assert_equal(self.test_rest_request(f"/headers/{UNKNOWN_PARAM}", query_params={"count": 1}), [])
self.test_rest_request(f"/block/{UNKNOWN_PARAM}", status=404, ret_type=RetType.OBJ)
# Check result if block is not in the active chain
self.nodes[0].invalidateblock(bb_hash)
- assert_equal(self.test_rest_request(f'/headers/1/{bb_hash}'), [])
+ assert_equal(self.test_rest_request(f'/headers/{bb_hash}', query_params={'count': 1}), [])
self.test_rest_request(f'/block/{bb_hash}')
self.nodes[0].reconsiderblock(bb_hash)
@@ -228,7 +238,7 @@ class RESTTest (BitcoinTestFramework):
response_bytes = response.read()
# Compare with block header
- response_header = self.test_rest_request(f"/headers/1/{bb_hash}", req_type=ReqType.BIN, ret_type=RetType.OBJ)
+ response_header = self.test_rest_request(f"/headers/{bb_hash}", req_type=ReqType.BIN, ret_type=RetType.OBJ, query_params={"count": 1})
assert_equal(int(response_header.getheader('content-length')), BLOCK_HEADER_SIZE)
response_header_bytes = response_header.read()
assert_equal(response_bytes[:BLOCK_HEADER_SIZE], response_header_bytes)
@@ -240,7 +250,7 @@ class RESTTest (BitcoinTestFramework):
assert_equal(response_bytes.hex().encode(), response_hex_bytes)
# Compare with hex block header
- response_header_hex = self.test_rest_request(f"/headers/1/{bb_hash}", req_type=ReqType.HEX, ret_type=RetType.OBJ)
+ response_header_hex = self.test_rest_request(f"/headers/{bb_hash}", req_type=ReqType.HEX, ret_type=RetType.OBJ, query_params={"count": 1})
assert_greater_than(int(response_header_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)
response_header_hex_bytes = response_header_hex.read(BLOCK_HEADER_SIZE*2)
assert_equal(response_bytes[:BLOCK_HEADER_SIZE].hex().encode(), response_header_hex_bytes)
@@ -267,7 +277,7 @@ class RESTTest (BitcoinTestFramework):
self.test_rest_request("/blockhashbyheight/", ret_type=RetType.OBJ, status=400)
# Compare with json block header
- json_obj = self.test_rest_request(f"/headers/1/{bb_hash}")
+ json_obj = self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 1})
assert_equal(len(json_obj), 1) # ensure that there is one header in the json response
assert_equal(json_obj[0]['hash'], bb_hash) # request/response hash should be the same
@@ -278,9 +288,9 @@ class RESTTest (BitcoinTestFramework):
# See if we can get 5 headers in one response
self.generate(self.nodes[1], 5)
- json_obj = self.test_rest_request(f"/headers/5/{bb_hash}")
+ json_obj = self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 5})
assert_equal(len(json_obj), 5) # now we should have 5 header objects
- json_obj = self.test_rest_request(f"/blockfilterheaders/basic/5/{bb_hash}")
+ json_obj = self.test_rest_request(f"/blockfilterheaders/basic/{bb_hash}", query_params={"count": 5})
first_filter_header = json_obj[0]
assert_equal(len(json_obj), 5) # now we should have 5 filter header objects
json_obj = self.test_rest_request(f"/blockfilter/basic/{bb_hash}")
@@ -294,7 +304,7 @@ class RESTTest (BitcoinTestFramework):
for num in ['5a', '-5', '0', '2001', '99999999999999999999999999999999999']:
assert_equal(
bytes(f'Header count is invalid or out of acceptable range (1-2000): {num}\r\n', 'ascii'),
- self.test_rest_request(f"/headers/{num}/{bb_hash}", ret_type=RetType.BYTES, status=400),
+ self.test_rest_request(f"/headers/{bb_hash}", ret_type=RetType.BYTES, status=400, query_params={"count": num}),
)
self.log.info("Test tx inclusion in the /mempool and /block URIs")
@@ -351,6 +361,11 @@ class RESTTest (BitcoinTestFramework):
json_obj = self.test_rest_request("/chaininfo")
assert_equal(json_obj['bestblockhash'], bb_hash)
+ # Test compatibility of deprecated and newer endpoints
+ self.log.info("Test compatibility of deprecated and newer endpoints")
+ assert_equal(self.test_rest_request(f"/headers/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/headers/1/{bb_hash}"))
+ assert_equal(self.test_rest_request(f"/blockfilterheaders/basic/{bb_hash}", query_params={"count": 1}), self.test_rest_request(f"/blockfilterheaders/basic/5/{bb_hash}"))
+
if __name__ == '__main__':
RESTTest().main()
diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py
index 1ee12c0040..7d8d10589b 100755
--- a/test/functional/interface_zmq.py
+++ b/test/functional/interface_zmq.py
@@ -23,6 +23,9 @@ from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
)
+from test_framework.wallet import (
+ MiniWallet,
+)
from test_framework.netutil import test_ipv6_local
from io import BytesIO
from time import sleep
@@ -100,8 +103,6 @@ class ZMQTestSetupBlock:
class ZMQTest (BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- if self.is_wallet_compiled():
- self.requires_wallet = True
# This test isn't testing txn relay/timing, so set whitelist on the
# peers for instant txn relay. This speeds up the test run time 2-3x.
self.extra_args = [["-whitelist=noban@127.0.0.1"]] * self.num_nodes
@@ -111,6 +112,7 @@ class ZMQTest (BitcoinTestFramework):
self.skip_if_no_bitcoind_zmq()
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
self.ctx = zmq.Context()
try:
self.test_basic()
@@ -211,25 +213,25 @@ class ZMQTest (BitcoinTestFramework):
assert_equal([txid.hex()], self.nodes[1].getblock(hash)["tx"])
- if self.is_wallet_compiled():
- self.log.info("Wait for tx from second node")
- payment_txid = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
- self.sync_all()
-
- # Should receive the broadcasted txid.
- txid = hashtx.receive()
- assert_equal(payment_txid, txid.hex())
+ self.wallet.rescan_utxos()
+ self.log.info("Wait for tx from second node")
+ payment_tx = self.wallet.send_self_transfer(from_node=self.nodes[1])
+ payment_txid = payment_tx['txid']
+ self.sync_all()
+ # Should receive the broadcasted txid.
+ txid = hashtx.receive()
+ assert_equal(payment_txid, txid.hex())
- # Should receive the broadcasted raw transaction.
- hex = rawtx.receive()
- assert_equal(payment_txid, hash256_reversed(hex).hex())
+ # Should receive the broadcasted raw transaction.
+ hex = rawtx.receive()
+ assert_equal(payment_tx['wtxid'], hash256_reversed(hex).hex())
- # Mining the block with this tx should result in second notification
- # after coinbase tx notification
- self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)
- hashtx.receive()
- txid = hashtx.receive()
- assert_equal(payment_txid, txid.hex())
+ # Mining the block with this tx should result in second notification
+ # after coinbase tx notification
+ self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)
+ hashtx.receive()
+ txid = hashtx.receive()
+ assert_equal(payment_txid, txid.hex())
self.log.info("Test the getzmqnotifications RPC")
@@ -243,9 +245,6 @@ class ZMQTest (BitcoinTestFramework):
assert_equal(self.nodes[1].getzmqnotifications(), [])
def test_reorg(self):
- if not self.is_wallet_compiled():
- self.log.info("Skipping reorg test because wallet is disabled")
- return
address = 'tcp://127.0.0.1:28333'
@@ -256,7 +255,7 @@ class ZMQTest (BitcoinTestFramework):
self.disconnect_nodes(0, 1)
# Generate 1 block in nodes[0] with 1 mempool tx and receive all notifications
- payment_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
+ payment_txid = self.wallet.send_self_transfer(from_node=self.nodes[0])['txid']
disconnect_block = self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE, sync_fun=self.no_op)[0]
disconnect_cb = self.nodes[0].getblock(disconnect_block)["tx"][0]
assert_equal(self.nodes[0].getbestblockhash(), hashblock.receive().hex())
@@ -325,126 +324,124 @@ class ZMQTest (BitcoinTestFramework):
assert_equal((self.nodes[1].getblockhash(block_count-1), "C", None), seq.receive_sequence())
assert_equal((self.nodes[1].getblockhash(block_count), "C", None), seq.receive_sequence())
- # Rest of test requires wallet functionality
- if self.is_wallet_compiled():
- self.log.info("Wait for tx from second node")
- payment_txid = self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=5.0, replaceable=True)
- self.sync_all()
- self.log.info("Testing sequence notifications with mempool sequence values")
-
- # Should receive the broadcasted txid.
- assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
- seq_num += 1
-
- self.log.info("Testing RBF notification")
- # Replace it to test eviction/addition notification
- rbf_info = self.nodes[1].bumpfee(payment_txid)
- self.sync_all()
- assert_equal((payment_txid, "R", seq_num), seq.receive_sequence())
- seq_num += 1
- assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
- seq_num += 1
-
- # Doesn't get published when mined, make a block and tx to "flush" the possibility
- # though the mempool sequence number does go up by the number of transactions
- # removed from the mempool by the block mining it.
- mempool_size = len(self.nodes[0].getrawmempool())
- c_block = self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)[0]
- # Make sure the number of mined transactions matches the number of txs out of mempool
- mempool_size_delta = mempool_size - len(self.nodes[0].getrawmempool())
- assert_equal(len(self.nodes[0].getblock(c_block)["tx"])-1, mempool_size_delta)
- seq_num += mempool_size_delta
- payment_txid_2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0)
- self.sync_all()
- assert_equal((c_block, "C", None), seq.receive_sequence())
- assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence())
- seq_num += 1
-
- # Spot check getrawmempool results that they only show up when asked for
- assert type(self.nodes[0].getrawmempool()) is list
- assert type(self.nodes[0].getrawmempool(mempool_sequence=False)) is list
- assert "mempool_sequence" not in self.nodes[0].getrawmempool(verbose=True)
- assert_raises_rpc_error(-8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True)
- assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], seq_num)
-
- self.log.info("Testing reorg notifications")
- # Manually invalidate the last block to test mempool re-entry
- # N.B. This part could be made more lenient in exact ordering
- # since it greatly depends on inner-workings of blocks/mempool
- # during "deep" re-orgs. Probably should "re-construct"
- # blockchain/mempool state from notifications instead.
- block_count = self.nodes[0].getblockcount()
- best_hash = self.nodes[0].getbestblockhash()
- self.nodes[0].invalidateblock(best_hash)
- sleep(2) # Bit of room to make sure transaction things happened
-
- # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
- # of the time they were gathered.
- assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
-
- assert_equal((best_hash, "D", None), seq.receive_sequence())
- assert_equal((rbf_info["txid"], "A", seq_num), seq.receive_sequence())
- seq_num += 1
-
- # Other things may happen but aren't wallet-deterministic so we don't test for them currently
- self.nodes[0].reconsiderblock(best_hash)
- self.generatetoaddress(self.nodes[1], 1, ADDRESS_BCRT1_UNSPENDABLE)
-
- self.log.info("Evict mempool transaction by block conflict")
- orig_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
-
- # More to be simply mined
- more_tx = []
- for _ in range(5):
- more_tx.append(self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.1))
-
- raw_tx = self.nodes[0].getrawtransaction(orig_txid)
- bump_info = self.nodes[0].bumpfee(orig_txid)
- # Mine the pre-bump tx
- txs_to_add = [raw_tx] + [self.nodes[0].getrawtransaction(txid) for txid in more_tx]
- block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1), txlist=txs_to_add)
- add_witness_commitment(block)
- block.solve()
- assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None)
- tip = self.nodes[0].getbestblockhash()
- assert_equal(int(tip, 16), block.sha256)
- orig_txid_2 = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True)
-
- # Flush old notifications until evicted tx original entry
+ self.log.info("Wait for tx from second node")
+ payment_tx = self.wallet.send_self_transfer(from_node=self.nodes[1])
+ payment_txid = payment_tx['txid']
+ self.sync_all()
+ self.log.info("Testing sequence notifications with mempool sequence values")
+
+ # Should receive the broadcasted txid.
+ assert_equal((payment_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ self.log.info("Testing RBF notification")
+ # Replace it to test eviction/addition notification
+ payment_tx['tx'].vout[0].nValue -= 1000
+ rbf_txid = self.nodes[1].sendrawtransaction(payment_tx['tx'].serialize().hex())
+ self.sync_all()
+ assert_equal((payment_txid, "R", seq_num), seq.receive_sequence())
+ seq_num += 1
+ assert_equal((rbf_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Doesn't get published when mined, make a block and tx to "flush" the possibility
+ # though the mempool sequence number does go up by the number of transactions
+ # removed from the mempool by the block mining it.
+ mempool_size = len(self.nodes[0].getrawmempool())
+ c_block = self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)[0]
+ # Make sure the number of mined transactions matches the number of txs out of mempool
+ mempool_size_delta = mempool_size - len(self.nodes[0].getrawmempool())
+ assert_equal(len(self.nodes[0].getblock(c_block)["tx"])-1, mempool_size_delta)
+ seq_num += mempool_size_delta
+ payment_txid_2 = self.wallet.send_self_transfer(from_node=self.nodes[1])['txid']
+ self.sync_all()
+ assert_equal((c_block, "C", None), seq.receive_sequence())
+ assert_equal((payment_txid_2, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Spot check getrawmempool results that they only show up when asked for
+ assert type(self.nodes[0].getrawmempool()) is list
+ assert type(self.nodes[0].getrawmempool(mempool_sequence=False)) is list
+ assert "mempool_sequence" not in self.nodes[0].getrawmempool(verbose=True)
+ assert_raises_rpc_error(-8, "Verbose results cannot contain mempool sequence values.", self.nodes[0].getrawmempool, True, True)
+ assert_equal(self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"], seq_num)
+
+ self.log.info("Testing reorg notifications")
+ # Manually invalidate the last block to test mempool re-entry
+ # N.B. This part could be made more lenient in exact ordering
+ # since it greatly depends on inner-workings of blocks/mempool
+ # during "deep" re-orgs. Probably should "re-construct"
+ # blockchain/mempool state from notifications instead.
+ block_count = self.nodes[0].getblockcount()
+ best_hash = self.nodes[0].getbestblockhash()
+ self.nodes[0].invalidateblock(best_hash)
+ sleep(2) # Bit of room to make sure transaction things happened
+
+ # Make sure getrawmempool mempool_sequence results aren't "queued" but immediately reflective
+ # of the time they were gathered.
+ assert self.nodes[0].getrawmempool(mempool_sequence=True)["mempool_sequence"] > seq_num
+
+ assert_equal((best_hash, "D", None), seq.receive_sequence())
+ assert_equal((rbf_txid, "A", seq_num), seq.receive_sequence())
+ seq_num += 1
+
+ # Other things may happen but aren't wallet-deterministic so we don't test for them currently
+ self.nodes[0].reconsiderblock(best_hash)
+ self.generatetoaddress(self.nodes[1], 1, ADDRESS_BCRT1_UNSPENDABLE)
+
+ self.log.info("Evict mempool transaction by block conflict")
+ orig_tx = self.wallet.send_self_transfer(from_node=self.nodes[0])
+ orig_txid = orig_tx['txid']
+
+ # More to be simply mined
+ more_tx = []
+ for _ in range(5):
+ more_tx.append(self.wallet.send_self_transfer(from_node=self.nodes[0]))
+
+ orig_tx['tx'].vout[0].nValue -= 1000
+ bump_txid = self.nodes[0].sendrawtransaction(orig_tx['tx'].serialize().hex())
+ # Mine the pre-bump tx
+ txs_to_add = [orig_tx['hex']] + [tx['hex'] for tx in more_tx]
+ block = create_block(int(self.nodes[0].getbestblockhash(), 16), create_coinbase(self.nodes[0].getblockcount()+1), txlist=txs_to_add)
+ add_witness_commitment(block)
+ block.solve()
+ assert_equal(self.nodes[0].submitblock(block.serialize().hex()), None)
+ tip = self.nodes[0].getbestblockhash()
+ assert_equal(int(tip, 16), block.sha256)
+ orig_txid_2 = self.wallet.send_self_transfer(from_node=self.nodes[0])['txid']
+
+ # Flush old notifications until evicted tx original entry
+ (hash_str, label, mempool_seq) = seq.receive_sequence()
+ while hash_str != orig_txid:
(hash_str, label, mempool_seq) = seq.receive_sequence()
- while hash_str != orig_txid:
- (hash_str, label, mempool_seq) = seq.receive_sequence()
- mempool_seq += 1
+ mempool_seq += 1
- # Added original tx
- assert_equal(label, "A")
- # More transactions to be simply mined
- for i in range(len(more_tx)):
- assert_equal((more_tx[i], "A", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
- # Bumped by rbf
- assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
- assert_equal((bump_info["txid"], "A", mempool_seq), seq.receive_sequence())
+ # Added original tx
+ assert_equal(label, "A")
+ # More transactions to be simply mined
+ for i in range(len(more_tx)):
+ assert_equal((more_tx[i]['txid'], "A", mempool_seq), seq.receive_sequence())
mempool_seq += 1
- # Conflict announced first, then block
- assert_equal((bump_info["txid"], "R", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
- assert_equal((tip, "C", None), seq.receive_sequence())
- mempool_seq += len(more_tx)
- # Last tx
- assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
- mempool_seq += 1
- self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)
- self.sync_all() # want to make sure we didn't break "consensus" for other tests
+ # Bumped by rbf
+ assert_equal((orig_txid, "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((bump_txid, "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ # Conflict announced first, then block
+ assert_equal((bump_txid, "R", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ assert_equal((tip, "C", None), seq.receive_sequence())
+ mempool_seq += len(more_tx)
+ # Last tx
+ assert_equal((orig_txid_2, "A", mempool_seq), seq.receive_sequence())
+ mempool_seq += 1
+ self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)
+ self.sync_all() # want to make sure we didn't break "consensus" for other tests
def test_mempool_sync(self):
"""
Use sequence notification plus getrawmempool sequence results to "sync mempool"
"""
- if not self.is_wallet_compiled():
- self.log.info("Skipping mempool sync test")
- return
self.log.info("Testing 'mempool sync' usage of sequence notifier")
[seq] = self.setup_zmq_test([("sequence", "tcp://127.0.0.1:28333")])
@@ -455,10 +452,10 @@ class ZMQTest (BitcoinTestFramework):
# Some transactions have been happening but we aren't consuming zmq notifications yet
# or we lost a ZMQ message somehow and want to start over
- txids = []
+ txs = []
num_txs = 5
for _ in range(num_txs):
- txids.append(self.nodes[1].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=1.0, replaceable=True))
+ txs.append(self.wallet.send_self_transfer(from_node=self.nodes[1]))
self.sync_all()
# 1) Consume backlog until we get a mempool sequence number
@@ -484,11 +481,12 @@ class ZMQTest (BitcoinTestFramework):
# Things continue to happen in the "interim" while waiting for snapshot results
# We have node 0 do all these to avoid p2p races with RBF announcements
for _ in range(num_txs):
- txids.append(self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True))
- self.nodes[0].bumpfee(txids[-1])
+ txs.append(self.wallet.send_self_transfer(from_node=self.nodes[0]))
+ txs[-1]['tx'].vout[0].nValue -= 1000
+ self.nodes[0].sendrawtransaction(txs[-1]['tx'].serialize().hex())
self.sync_all()
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_UNSPENDABLE)
- final_txid = self.nodes[0].sendtoaddress(address=self.nodes[0].getnewaddress(), amount=0.1, replaceable=True)
+ final_txid = self.wallet.send_self_transfer(from_node=self.nodes[0])['txid']
# 3) Consume ZMQ backlog until we get to "now" for the mempool snapshot
while True:
diff --git a/test/functional/mempool_package_onemore.py b/test/functional/mempool_package_onemore.py
index a6fb1dcf35..423a5bf2ee 100755
--- a/test/functional/mempool_package_onemore.py
+++ b/test/functional/mempool_package_onemore.py
@@ -7,74 +7,68 @@
size.
"""
-from decimal import Decimal
-
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
- chain_transaction,
)
+from test_framework.wallet import MiniWallet
+
MAX_ANCESTORS = 25
MAX_DESCENDANTS = 25
+
class MempoolPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [["-maxorphantx=1000"]]
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ def chain_tx(self, utxos_to_spend, *, num_outputs=1):
+ return self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ utxos_to_spend=utxos_to_spend,
+ num_outputs=num_outputs)['new_utxos']
def run_test(self):
- # Mine some blocks and have them mature.
- self.generate(self.nodes[0], COINBASE_MATURITY + 1)
- utxo = self.nodes[0].listunspent(10)
- txid = utxo[0]['txid']
- vout = utxo[0]['vout']
- value = utxo[0]['amount']
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.rescan_utxos()
- fee = Decimal("0.0002")
# MAX_ANCESTORS transactions off a confirmed tx should be fine
chain = []
+ utxo = self.wallet.get_utxo()
for _ in range(4):
- (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [vout], value, fee, 2)
- vout = 0
- value = sent_value
- chain.append([txid, value])
+ utxo, utxo2 = self.chain_tx([utxo], num_outputs=2)
+ chain.append(utxo2)
for _ in range(MAX_ANCESTORS - 4):
- (txid, sent_value) = chain_transaction(self.nodes[0], [txid], [0], value, fee, 1)
- value = sent_value
- chain.append([txid, value])
- (second_chain, second_chain_value) = chain_transaction(self.nodes[0], [utxo[1]['txid']], [utxo[1]['vout']], utxo[1]['amount'], fee, 1)
+ utxo, = self.chain_tx([utxo])
+ chain.append(utxo)
+ second_chain, = self.chain_tx([self.wallet.get_utxo()])
# Check mempool has MAX_ANCESTORS + 1 transactions in it
assert_equal(len(self.nodes[0].getrawmempool()), MAX_ANCESTORS + 1)
# Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", chain_transaction, self.nodes[0], [txid], [0], value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]", self.chain_tx, [utxo])
# ...even if it chains on from some point in the middle of the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[2][0]], [1], chain[2][1], fee, 1)
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[1][0]], [1], chain[1][1], fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[2]])
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[1]])
# ...even if it chains on to two parent transactions with one in the chain.
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0], second_chain], [1, 0], chain[0][1] + second_chain_value, fee, 1)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[0], second_chain])
# ...especially if its > 40k weight
- assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", chain_transaction, self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 350)
+ assert_raises_rpc_error(-26, "too-long-mempool-chain, too many descendants", self.chain_tx, [chain[0]], num_outputs=350)
# But not if it chains directly off the first transaction
- (replacable_txid, replacable_orig_value) = chain_transaction(self.nodes[0], [chain[0][0]], [1], chain[0][1], fee, 1)
+ replacable_tx = self.wallet.send_self_transfer_multi(from_node=self.nodes[0], utxos_to_spend=[chain[0]])['tx']
# and the second chain should work just fine
- chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)
+ self.chain_tx([second_chain])
# Make sure we can RBF the chain which used our carve-out rule
- second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['address']: replacable_orig_value - (Decimal(1) / Decimal(100))}
- second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
- signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
- self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
+ replacable_tx.vout[0].nValue -= 1000000
+ self.nodes[0].sendrawtransaction(replacable_tx.serialize().hex())
# Finally, check that we added two transactions
assert_equal(len(self.nodes[0].getrawmempool()), MAX_ANCESTORS + 3)
+
if __name__ == '__main__':
MempoolPackagesTest().main()
diff --git a/test/functional/mempool_unbroadcast.py b/test/functional/mempool_unbroadcast.py
index adf7326dac..37ef4a9157 100755
--- a/test/functional/mempool_unbroadcast.py
+++ b/test/functional/mempool_unbroadcast.py
@@ -9,21 +9,20 @@ import time
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- create_confirmed_utxos,
-)
+from test_framework.util import assert_equal
+from test_framework.wallet import MiniWallet
MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
class MempoolUnbroadcastTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ if self.is_wallet_compiled():
+ self.requires_wallet = True
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+ self.wallet.rescan_utxos()
self.test_broadcast()
self.test_txn_removal()
@@ -31,30 +30,25 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.log.info("Test that mempool reattempts delivery of locally submitted transaction")
node = self.nodes[0]
- min_relay_fee = node.getnetworkinfo()["relayfee"]
- utxos = create_confirmed_utxos(self, min_relay_fee, node, 10)
-
self.disconnect_nodes(0, 1)
self.log.info("Generate transactions that only node 0 knows about")
- # generate a wallet txn
- addr = node.getnewaddress()
- wallet_tx_hsh = node.sendtoaddress(addr, 0.0001)
+ if self.is_wallet_compiled():
+ # generate a wallet txn
+ addr = node.getnewaddress()
+ wallet_tx_hsh = node.sendtoaddress(addr, 0.0001)
# generate a txn using sendrawtransaction
- us0 = utxos.pop()
- inputs = [{"txid": us0["txid"], "vout": us0["vout"]}]
- outputs = {addr: 0.0001}
- tx = node.createrawtransaction(inputs, outputs)
- node.settxfee(min_relay_fee)
- txF = node.fundrawtransaction(tx)
- txFS = node.signrawtransactionwithwallet(txF["hex"])
+ txFS = self.wallet.create_self_transfer(from_node=node)
rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
# check transactions are in unbroadcast using rpc
mempoolinfo = self.nodes[0].getmempoolinfo()
- assert_equal(mempoolinfo['unbroadcastcount'], 2)
+ unbroadcast_count = 1
+ if self.is_wallet_compiled():
+ unbroadcast_count += 1
+ assert_equal(mempoolinfo['unbroadcastcount'], unbroadcast_count)
mempool = self.nodes[0].getrawmempool(True)
for tx in mempool:
assert_equal(mempool[tx]['unbroadcast'], True)
@@ -62,7 +56,8 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
# check that second node doesn't have these two txns
mempool = self.nodes[1].getrawmempool()
assert rpc_tx_hsh not in mempool
- assert wallet_tx_hsh not in mempool
+ if self.is_wallet_compiled():
+ assert wallet_tx_hsh not in mempool
# ensure that unbroadcast txs are persisted to mempool.dat
self.restart_node(0)
@@ -75,7 +70,8 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
self.sync_mempools(timeout=30)
mempool = self.nodes[1].getrawmempool()
assert rpc_tx_hsh in mempool
- assert wallet_tx_hsh in mempool
+ if self.is_wallet_compiled():
+ assert wallet_tx_hsh in mempool
# check that transactions are no longer in first node's unbroadcast set
mempool = self.nodes[0].getrawmempool(True)
@@ -102,8 +98,7 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
# since the node doesn't have any connections, it will not receive
# any GETDATAs & thus the transaction will remain in the unbroadcast set.
- addr = node.getnewaddress()
- txhsh = node.sendtoaddress(addr, 0.0001)
+ txhsh = self.wallet.send_self_transfer(from_node=node)["txid"]
# check transaction was removed from unbroadcast set due to presence in
# a block
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 6f2ac805a0..a15fbe5a24 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -4,11 +4,15 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the prioritisetransaction mining RPC."""
+from decimal import Decimal
import time
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.messages import COIN, MAX_BLOCK_WEIGHT
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, create_confirmed_utxos, create_lots_of_big_transactions, gen_return_txouts
+from test_framework.wallet import MiniWallet
+
class PrioritiseTransactionTest(BitcoinTestFramework):
def set_test_params(self):
@@ -23,7 +27,84 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
+ def test_diamond(self):
+ self.log.info("Test diamond-shape package with priority")
+ self.generate(self.wallet, COINBASE_MATURITY + 1)
+ mock_time = int(time.time())
+ self.nodes[0].setmocktime(mock_time)
+
+ # tx_a
+ # / \
+ # / \
+ # tx_b tx_c
+ # \ /
+ # \ /
+ # tx_d
+
+ tx_o_a = self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ num_outputs=2,
+ )
+ txid_a = tx_o_a["txid"]
+
+ tx_o_b, tx_o_c = [self.wallet.send_self_transfer(
+ from_node=self.nodes[0],
+ utxo_to_spend=u,
+ ) for u in tx_o_a["new_utxos"]]
+ txid_b = tx_o_b["txid"]
+ txid_c = tx_o_c["txid"]
+
+ tx_o_d = self.wallet.send_self_transfer_multi(
+ from_node=self.nodes[0],
+ utxos_to_spend=[
+ self.wallet.get_utxo(txid=txid_b),
+ self.wallet.get_utxo(txid=txid_c),
+ ],
+ )
+ txid_d = tx_o_d["txid"]
+
+ self.log.info("Test priority while txs are in mempool")
+ raw_before = self.nodes[0].getrawmempool(verbose=True)
+ fee_delta_b = Decimal(9999) / COIN
+ fee_delta_c_1 = Decimal(-1234) / COIN
+ fee_delta_c_2 = Decimal(8888) / COIN
+ self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
+ self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
+ self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
+ raw_before[txid_a]["fees"]["descendant"] += fee_delta_b + fee_delta_c_1 + fee_delta_c_2
+ raw_before[txid_b]["fees"]["modified"] += fee_delta_b
+ raw_before[txid_b]["fees"]["ancestor"] += fee_delta_b
+ raw_before[txid_b]["fees"]["descendant"] += fee_delta_b
+ raw_before[txid_c]["fees"]["modified"] += fee_delta_c_1 + fee_delta_c_2
+ raw_before[txid_c]["fees"]["ancestor"] += fee_delta_c_1 + fee_delta_c_2
+ raw_before[txid_c]["fees"]["descendant"] += fee_delta_c_1 + fee_delta_c_2
+ raw_before[txid_d]["fees"]["ancestor"] += fee_delta_b + fee_delta_c_1 + fee_delta_c_2
+ raw_after = self.nodes[0].getrawmempool(verbose=True)
+ assert_equal(raw_before[txid_a], raw_after[txid_a])
+ assert_equal(raw_before, raw_after)
+
+ self.log.info("Test priority while txs are not in mempool")
+ self.restart_node(0, extra_args=["-nopersistmempool"])
+ self.nodes[0].setmocktime(mock_time)
+ assert_equal(self.nodes[0].getmempoolinfo()["size"], 0)
+ self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
+ self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
+ self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
+ for t in [tx_o_a["hex"], tx_o_b["hex"], tx_o_c["hex"], tx_o_d["hex"]]:
+ self.nodes[0].sendrawtransaction(t)
+ raw_after = self.nodes[0].getrawmempool(verbose=True)
+ assert_equal(raw_before[txid_a], raw_after[txid_a])
+ assert_equal(raw_before, raw_after)
+
+ # Clear mempool
+ self.generate(self.nodes[0], 1)
+
+ # Use default extra_args
+ self.restart_node(0)
+
def run_test(self):
+ self.wallet = MiniWallet(self.nodes[0])
+
# Test `prioritisetransaction` required parameters
assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction)
assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '')
@@ -44,6 +125,8 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
# Test `prioritisetransaction` invalid `fee_delta`
assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo')
+ self.test_diamond()
+
self.txouts = gen_return_txouts()
self.relayfee = self.nodes[0].getnetworkinfo()['relayfee']
diff --git a/test/functional/p2p_blockfilters.py b/test/functional/p2p_blockfilters.py
index e73fad439f..e45cef65bd 100755
--- a/test/functional/p2p_blockfilters.py
+++ b/test/functional/p2p_blockfilters.py
@@ -244,6 +244,12 @@ class CompactFiltersTest(BitcoinTestFramework):
peer_0.send_message(request)
peer_0.wait_for_disconnect()
+ self.log.info("Test -peerblockfilters without -blockfilterindex raises an error")
+ self.stop_node(0)
+ self.nodes[0].extra_args = ["-peerblockfilters"]
+ msg = "Error: Cannot set -peerblockfilters without -blockfilterindex."
+ self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
+
def compute_last_header(prev_header, hashes):
"""Compute the last filter header from a starting header and a sequence of filter hashes."""
diff --git a/test/functional/p2p_blocksonly.py b/test/functional/p2p_blocksonly.py
index 6f142f23f2..12ee4b3c27 100755
--- a/test/functional/p2p_blocksonly.py
+++ b/test/functional/p2p_blocksonly.py
@@ -94,7 +94,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
self.nodes[0].sendrawtransaction(tx_hex)
- # Bump time forward to ensure nNextInvSend timer pops
+ # Bump time forward to ensure m_next_inv_send_time timer pops
self.nodes[0].setmocktime(int(time.time()) + 60)
conn.sync_send_with_ping()
diff --git a/test/functional/p2p_message_capture.py b/test/functional/p2p_message_capture.py
index edde9a6ecf..0a7ae44de4 100755
--- a/test/functional/p2p_message_capture.py
+++ b/test/functional/p2p_message_capture.py
@@ -20,7 +20,7 @@ LENGTH_SIZE = 4
MSGTYPE_SIZE = 12
def mini_parser(dat_file):
- """Parse a data file created by CaptureMessage.
+ """Parse a data file created by CaptureMessageToFile.
From the data file we'll only check the structure.
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 2d96ba74b5..193bd3f1cd 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -69,7 +69,14 @@ class BlockchainTest(BitcoinTestFramework):
self.wallet = MiniWallet(self.nodes[0])
self.mine_chain()
self._test_max_future_block_time()
- self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete
+ self.restart_node(
+ 0,
+ extra_args=[
+ "-stopatheight=207",
+ "-checkblocks=-1", # Check all blocks
+ "-prune=1", # Set pruning after rescan is complete
+ ],
+ )
self._test_getblockchaininfo()
self._test_getchaintxstats()
@@ -193,7 +200,7 @@ class BlockchainTest(BitcoinTestFramework):
'timeout': 0x7fffffffffffffff, # testdummy does not have a timeout so is set to the max int64 value
'min_activation_height': 0,
'status': 'started',
- 'status-next': status_next,
+ 'status_next': status_next,
'since': 144,
'statistics': {
'period': 144,
@@ -213,7 +220,7 @@ class BlockchainTest(BitcoinTestFramework):
'timeout': 9223372036854775807,
'min_activation_height': 0,
'status': 'active',
- 'status-next': 'active',
+ 'status_next': 'active',
'since': 0,
},
'height': 0,
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 1a3d14100f..1695acaaa8 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -18,15 +18,18 @@ from test_framework.util import (
assert_equal,
)
from test_framework.wallet_util import bytes_to_wif
+from test_framework.wallet import (
+ MiniWallet,
+ getnewdestination,
+)
class RpcCreateMultiSigTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 3
self.supports_cli = False
-
- def skip_test_if_missing_module(self):
- self.skip_if_no_wallet()
+ if self.is_bdb_compiled():
+ self.requires_wallet = True
def get_keys(self):
self.pub = []
@@ -37,15 +40,20 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
k.generate()
self.pub.append(k.get_pubkey().get_bytes().hex())
self.priv.append(bytes_to_wif(k.get_bytes(), k.is_compressed))
- self.final = node2.getnewaddress()
+ if self.is_bdb_compiled():
+ self.final = node2.getnewaddress()
+ else:
+ self.final = getnewdestination()[2]
def run_test(self):
node0, node1, node2 = self.nodes
+ self.wallet = MiniWallet(test_node=node0)
- self.check_addmultisigaddress_errors()
+ if self.is_bdb_compiled():
+ self.check_addmultisigaddress_errors()
self.log.info('Generating blocks ...')
- self.generate(node0, 149)
+ self.generate(self.wallet, 149)
self.moved = 0
for self.nkeys in [3, 5]:
@@ -53,14 +61,14 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
for self.output_type in ["bech32", "p2sh-segwit", "legacy"]:
self.get_keys()
self.do_multisig()
-
- self.checkbalances()
+ if self.is_bdb_compiled():
+ self.checkbalances()
# Test mixed compressed and uncompressed pubkeys
self.log.info('Mixed compressed and uncompressed multisigs are not allowed')
- pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey']
- pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey']
- pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey']
+ pk0 = getnewdestination()[0].hex()
+ pk1 = getnewdestination()[0].hex()
+ pk2 = getnewdestination()[0].hex()
# decompress pk2
pk_obj = ECPubKey()
@@ -68,26 +76,30 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
pk_obj.compressed = False
pk2 = pk_obj.get_bytes().hex()
- node0.createwallet(wallet_name='wmulti0', disable_private_keys=True)
- wmulti0 = node0.get_wallet_rpc('wmulti0')
+ if self.is_bdb_compiled():
+ node0.createwallet(wallet_name='wmulti0', disable_private_keys=True)
+ wmulti0 = node0.get_wallet_rpc('wmulti0')
# Check all permutations of keys because order matters apparently
for keys in itertools.permutations([pk0, pk1, pk2]):
# Results should be the same as this legacy one
legacy_addr = node0.createmultisig(2, keys, 'legacy')['address']
- result = wmulti0.addmultisigaddress(2, keys, '', 'legacy')
- assert_equal(legacy_addr, result['address'])
- assert 'warnings' not in result
+
+ if self.is_bdb_compiled():
+ result = wmulti0.addmultisigaddress(2, keys, '', 'legacy')
+ assert_equal(legacy_addr, result['address'])
+ assert 'warnings' not in result
# Generate addresses with the segwit types. These should all make legacy addresses
for addr_type in ['bech32', 'p2sh-segwit']:
- result = wmulti0.createmultisig(2, keys, addr_type)
+ result = self.nodes[0].createmultisig(2, keys, addr_type)
assert_equal(legacy_addr, result['address'])
assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
- result = wmulti0.addmultisigaddress(2, keys, '', addr_type)
- assert_equal(legacy_addr, result['address'])
- assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
+ if self.is_bdb_compiled():
+ result = wmulti0.addmultisigaddress(2, keys, '', addr_type)
+ assert_equal(legacy_addr, result['address'])
+ assert_equal(result['warnings'], ["Unable to make chosen address type, please ensure no uncompressed public keys are present."])
self.log.info('Testing sortedmulti descriptors with BIP 67 test vectors')
with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data/rpc_bip67.json'), encoding='utf-8') as f:
@@ -126,26 +138,29 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
bal0 = node0.getbalance()
bal1 = node1.getbalance()
bal2 = node2.getbalance()
+ balw = self.wallet.get_balance()
height = node0.getblockchaininfo()["blocks"]
assert 150 < height < 350
total = 149 * 50 + (height - 149 - 100) * 25
assert bal1 == 0
assert bal2 == self.moved
- assert bal0 + bal1 + bal2 == total
+ assert_equal(bal0 + bal1 + bal2 + balw, total)
def do_multisig(self):
node0, node1, node2 = self.nodes
- if 'wmulti' not in node1.listwallets():
- try:
- node1.loadwallet('wmulti')
- except JSONRPCException as e:
- path = os.path.join(self.options.tmpdir, "node1", "regtest", "wallets", "wmulti")
- if e.error['code'] == -18 and "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path) in e.error['message']:
- node1.createwallet(wallet_name='wmulti', disable_private_keys=True)
- else:
- raise
- wmulti = node1.get_wallet_rpc('wmulti')
+
+ if self.is_bdb_compiled():
+ if 'wmulti' not in node1.listwallets():
+ try:
+ node1.loadwallet('wmulti')
+ except JSONRPCException as e:
+ path = os.path.join(self.options.tmpdir, "node1", "regtest", "wallets", "wmulti")
+ if e.error['code'] == -18 and "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path) in e.error['message']:
+ node1.createwallet(wallet_name='wmulti', disable_private_keys=True)
+ else:
+ raise
+ wmulti = node1.get_wallet_rpc('wmulti')
# Construct the expected descriptor
desc = 'multi({},{})'.format(self.nsigs, ','.join(self.pub))
@@ -164,17 +179,19 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
if self.output_type == 'bech32':
assert madd[0:4] == "bcrt" # actually a bech32 address
- # compare against addmultisigaddress
- msigw = wmulti.addmultisigaddress(self.nsigs, self.pub, None, self.output_type)
- maddw = msigw["address"]
- mredeemw = msigw["redeemScript"]
- assert_equal(desc, drop_origins(msigw['descriptor']))
- # addmultisigiaddress and createmultisig work the same
- assert maddw == madd
- assert mredeemw == mredeem
-
- txid = node0.sendtoaddress(madd, 40)
-
+ if self.is_bdb_compiled():
+ # compare against addmultisigaddress
+ msigw = wmulti.addmultisigaddress(self.nsigs, self.pub, None, self.output_type)
+ maddw = msigw["address"]
+ mredeemw = msigw["redeemScript"]
+ assert_equal(desc, drop_origins(msigw['descriptor']))
+ # addmultisigiaddress and createmultisig work the same
+ assert maddw == madd
+ assert mredeemw == mredeem
+ wmulti.unloadwallet()
+
+ spk = bytes.fromhex(node0.validateaddress(madd)["scriptPubKey"])
+ txid, _ = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=spk, amount=1300)
tx = node0.getrawtransaction(txid, True)
vout = [v["n"] for v in tx["vout"] if madd == v["scriptPubKey"]["address"]]
assert len(vout) == 1
@@ -225,8 +242,6 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
txinfo = node0.getrawtransaction(tx, True, blk)
self.log.info("n/m=%d/%d %s size=%d vsize=%d weight=%d" % (self.nsigs, self.nkeys, self.output_type, txinfo["size"], txinfo["vsize"], txinfo["weight"]))
- wmulti.unloadwallet()
-
if __name__ == '__main__':
RpcCreateMultiSigTest().main()
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index 56f596d419..343cb73989 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -52,7 +52,7 @@ class DecodeScriptTest(BitcoinTestFramework):
rpc_result = self.nodes[0].decodescript('5100')
assert_equal('1 0', rpc_result['asm'])
- # null data scriptSig - no such thing because null data scripts can not be spent.
+ # null data scriptSig - no such thing because null data scripts cannot be spent.
# thus, no test case for that standard transaction type is here.
def decodescript_script_pub_key(self):
diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py
index 1721b6ffe8..4ca84748b2 100755
--- a/test/functional/rpc_dumptxoutset.py
+++ b/test/functional/rpc_dumptxoutset.py
@@ -37,16 +37,16 @@ class DumptxoutsetTest(BitcoinTestFramework):
# Blockhash should be deterministic based on mocked time.
assert_equal(
out['base_hash'],
- '6fd417acba2a8738b06fee43330c50d58e6a725046c3d843c8dd7e51d46d1ed6')
+ '09abf0e7b510f61ca6cf33bab104e9ee99b3528b371d27a2d4b39abb800fba7e')
with open(str(expected_path), 'rb') as f:
digest = hashlib.sha256(f.read()).hexdigest()
# UTXO snapshot hash should be deterministic based on mocked time.
assert_equal(
- digest, '7ae82c986fa5445678d2a21453bb1c86d39e47af13da137640c2b1cf8093691c')
+ digest, 'b1bacb602eacf5fbc9a7c2ef6eeb0d229c04e98bdf0c2ea5929012cd0eae3830')
assert_equal(
- out['txoutset_hash'], 'd4b614f476b99a6e569973bf1c0120d88b1a168076f8ce25691fb41dd1cef149')
+ out['txoutset_hash'], '1f7e3befd45dc13ae198dfbb22869a9c5c4196f8e9ef9735831af1288033f890')
assert_equal(out['nchaintx'], 101)
# Specifying a path to an existing file will fail.
diff --git a/test/functional/rpc_generate.py b/test/functional/rpc_generate.py
index 47d7814da3..2b1dd20ea1 100755
--- a/test/functional/rpc_generate.py
+++ b/test/functional/rpc_generate.py
@@ -2,9 +2,10 @@
# Copyright (c) 2020-2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test generate RPC."""
+"""Test generate* RPCs."""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.wallet import MiniWallet
from test_framework.util import (
assert_equal,
assert_raises_rpc_error,
@@ -16,6 +17,94 @@ class RPCGenerateTest(BitcoinTestFramework):
self.num_nodes = 1
def run_test(self):
+ self.test_generatetoaddress()
+ self.test_generate()
+ self.test_generateblock()
+
+ def test_generatetoaddress(self):
+ self.generatetoaddress(self.nodes[0], 1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
+ assert_raises_rpc_error(-5, "Invalid address", self.generatetoaddress, self.nodes[0], 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
+
+ def test_generateblock(self):
+ node = self.nodes[0]
+ miniwallet = MiniWallet(node)
+ miniwallet.rescan_utxos()
+
+ self.log.info('Generate an empty block to address')
+ address = miniwallet.get_address()
+ hash = self.generateblock(node, output=address, transactions=[])['hash']
+ block = node.getblock(blockhash=hash, verbose=2)
+ assert_equal(len(block['tx']), 1)
+ assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
+
+ self.log.info('Generate an empty block to a descriptor')
+ hash = self.generateblock(node, 'addr(' + address + ')', [])['hash']
+ block = node.getblock(blockhash=hash, verbosity=2)
+ assert_equal(len(block['tx']), 1)
+ assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
+
+ self.log.info('Generate an empty block to a combo descriptor with compressed pubkey')
+ combo_key = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
+ combo_address = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080'
+ hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
+ block = node.getblock(hash, 2)
+ assert_equal(len(block['tx']), 1)
+ assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
+
+ self.log.info('Generate an empty block to a combo descriptor with uncompressed pubkey')
+ combo_key = '0408ef68c46d20596cc3f6ddf7c8794f71913add807f1dc55949fa805d764d191c0b7ce6894c126fce0babc6663042f3dde9b0cf76467ea315514e5a6731149c67'
+ combo_address = 'mkc9STceoCcjoXEXe6cm66iJbmjM6zR9B2'
+ hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
+ block = node.getblock(hash, 2)
+ assert_equal(len(block['tx']), 1)
+ assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
+
+ # Generate some extra mempool transactions to verify they don't get mined
+ for _ in range(10):
+ miniwallet.send_self_transfer(from_node=node)
+
+ self.log.info('Generate block with txid')
+ txid = miniwallet.send_self_transfer(from_node=node)['txid']
+ hash = self.generateblock(node, address, [txid])['hash']
+ block = node.getblock(hash, 1)
+ assert_equal(len(block['tx']), 2)
+ assert_equal(block['tx'][1], txid)
+
+ self.log.info('Generate block with raw tx')
+ rawtx = miniwallet.create_self_transfer()['hex']
+ hash = self.generateblock(node, address, [rawtx])['hash']
+
+ block = node.getblock(hash, 1)
+ assert_equal(len(block['tx']), 2)
+ txid = block['tx'][1]
+ assert_equal(node.getrawtransaction(txid=txid, verbose=False, blockhash=hash), rawtx)
+
+ self.log.info('Fail to generate block with out of order txs')
+ txid1 = miniwallet.send_self_transfer(from_node=node)['txid']
+ utxo1 = miniwallet.get_utxo(txid=txid1)
+ rawtx2 = miniwallet.create_self_transfer(utxo_to_spend=utxo1)['hex']
+ assert_raises_rpc_error(-25, 'TestBlockValidity failed: bad-txns-inputs-missingorspent', self.generateblock, node, address, [rawtx2, txid1])
+
+ self.log.info('Fail to generate block with txid not in mempool')
+ missing_txid = '0000000000000000000000000000000000000000000000000000000000000000'
+ assert_raises_rpc_error(-5, 'Transaction ' + missing_txid + ' not in mempool.', self.generateblock, node, address, [missing_txid])
+
+ self.log.info('Fail to generate block with invalid raw tx')
+ invalid_raw_tx = '0000'
+ assert_raises_rpc_error(-22, 'Transaction decode failed for ' + invalid_raw_tx, self.generateblock, node, address, [invalid_raw_tx])
+
+ self.log.info('Fail to generate block with invalid address/descriptor')
+ assert_raises_rpc_error(-5, 'Invalid address or descriptor', self.generateblock, node, '1234', [])
+
+ self.log.info('Fail to generate block with a ranged descriptor')
+ ranged_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0/*)'
+ assert_raises_rpc_error(-8, 'Ranged descriptor not accepted. Maybe pass through deriveaddresses first?', self.generateblock, node, ranged_descriptor, [])
+
+ self.log.info('Fail to generate block with a descriptor missing a private key')
+ child_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0\'/0)'
+ assert_raises_rpc_error(-5, 'Cannot derive script without private keys', self.generateblock, node, child_descriptor, [])
+
+ def test_generate(self):
message = (
"generate\n\n"
"has been replaced by the -generate "
diff --git a/test/functional/rpc_generateblock.py b/test/functional/rpc_generateblock.py
deleted file mode 100755
index 7eeb745817..0000000000
--- a/test/functional/rpc_generateblock.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2020-2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-'''Test generateblock rpc.
-'''
-
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.wallet import MiniWallet
-from test_framework.util import (
- assert_equal,
- assert_raises_rpc_error,
-)
-
-
-class GenerateBlockTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 1
-
- def run_test(self):
- node = self.nodes[0]
- miniwallet = MiniWallet(node)
- miniwallet.rescan_utxos()
-
- self.log.info('Generate an empty block to address')
- address = miniwallet.get_address()
- hash = self.generateblock(node, output=address, transactions=[])['hash']
- block = node.getblock(blockhash=hash, verbose=2)
- assert_equal(len(block['tx']), 1)
- assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
-
- self.log.info('Generate an empty block to a descriptor')
- hash = self.generateblock(node, 'addr(' + address + ')', [])['hash']
- block = node.getblock(blockhash=hash, verbosity=2)
- assert_equal(len(block['tx']), 1)
- assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], address)
-
- self.log.info('Generate an empty block to a combo descriptor with compressed pubkey')
- combo_key = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
- combo_address = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080'
- hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
- block = node.getblock(hash, 2)
- assert_equal(len(block['tx']), 1)
- assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
-
- self.log.info('Generate an empty block to a combo descriptor with uncompressed pubkey')
- combo_key = '0408ef68c46d20596cc3f6ddf7c8794f71913add807f1dc55949fa805d764d191c0b7ce6894c126fce0babc6663042f3dde9b0cf76467ea315514e5a6731149c67'
- combo_address = 'mkc9STceoCcjoXEXe6cm66iJbmjM6zR9B2'
- hash = self.generateblock(node, 'combo(' + combo_key + ')', [])['hash']
- block = node.getblock(hash, 2)
- assert_equal(len(block['tx']), 1)
- assert_equal(block['tx'][0]['vout'][0]['scriptPubKey']['address'], combo_address)
-
- # Generate some extra mempool transactions to verify they don't get mined
- for _ in range(10):
- miniwallet.send_self_transfer(from_node=node)
-
- self.log.info('Generate block with txid')
- txid = miniwallet.send_self_transfer(from_node=node)['txid']
- hash = self.generateblock(node, address, [txid])['hash']
- block = node.getblock(hash, 1)
- assert_equal(len(block['tx']), 2)
- assert_equal(block['tx'][1], txid)
-
- self.log.info('Generate block with raw tx')
- rawtx = miniwallet.create_self_transfer()['hex']
- hash = self.generateblock(node, address, [rawtx])['hash']
-
- block = node.getblock(hash, 1)
- assert_equal(len(block['tx']), 2)
- txid = block['tx'][1]
- assert_equal(node.getrawtransaction(txid=txid, verbose=False, blockhash=hash), rawtx)
-
- self.log.info('Fail to generate block with out of order txs')
- txid1 = miniwallet.send_self_transfer(from_node=node)['txid']
- utxo1 = miniwallet.get_utxo(txid=txid1)
- rawtx2 = miniwallet.create_self_transfer(utxo_to_spend=utxo1)['hex']
- assert_raises_rpc_error(-25, 'TestBlockValidity failed: bad-txns-inputs-missingorspent', self.generateblock, node, address, [rawtx2, txid1])
-
- self.log.info('Fail to generate block with txid not in mempool')
- missing_txid = '0000000000000000000000000000000000000000000000000000000000000000'
- assert_raises_rpc_error(-5, 'Transaction ' + missing_txid + ' not in mempool.', self.generateblock, node, address, [missing_txid])
-
- self.log.info('Fail to generate block with invalid raw tx')
- invalid_raw_tx = '0000'
- assert_raises_rpc_error(-22, 'Transaction decode failed for ' + invalid_raw_tx, self.generateblock, node, address, [invalid_raw_tx])
-
- self.log.info('Fail to generate block with invalid address/descriptor')
- assert_raises_rpc_error(-5, 'Invalid address or descriptor', self.generateblock, node, '1234', [])
-
- self.log.info('Fail to generate block with a ranged descriptor')
- ranged_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0/*)'
- assert_raises_rpc_error(-8, 'Ranged descriptor not accepted. Maybe pass through deriveaddresses first?', self.generateblock, node, ranged_descriptor, [])
-
- self.log.info('Fail to generate block with a descriptor missing a private key')
- child_descriptor = 'pkh(tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp/0\'/0)'
- assert_raises_rpc_error(-5, 'Cannot derive script without private keys', self.generateblock, node, child_descriptor, [])
-
-if __name__ == '__main__':
- GenerateBlockTest().main()
diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py
index ccb380e25b..3b6413d4a6 100755
--- a/test/functional/rpc_help.py
+++ b/test/functional/rpc_help.py
@@ -100,7 +100,7 @@ class HelpRpcTest(BitcoinTestFramework):
# command titles
titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')]
- components = ['Blockchain', 'Control', 'Generating', 'Mining', 'Network', 'Rawtransactions', 'Util']
+ components = ['Blockchain', 'Control', 'Mining', 'Network', 'Rawtransactions', 'Util']
if self.is_wallet_compiled():
components.append('Wallet')
diff --git a/test/functional/rpc_uptime.py b/test/functional/rpc_uptime.py
index 1a82d1fa41..024e8aec1a 100755
--- a/test/functional/rpc_uptime.py
+++ b/test/functional/rpc_uptime.py
@@ -23,7 +23,7 @@ class UptimeTest(BitcoinTestFramework):
self._test_uptime()
def _test_negative_time(self):
- assert_raises_rpc_error(-8, "Mocktime can not be negative: -1.", self.nodes[0].setmocktime, -1)
+ assert_raises_rpc_error(-8, "Mocktime cannot be negative: -1.", self.nodes[0].setmocktime, -1)
def _test_uptime(self):
wait_time = 10
diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py
index 7791ae5392..2b70eab4e4 100644
--- a/test/functional/test_framework/script.py
+++ b/test/functional/test_framework/script.py
@@ -27,6 +27,7 @@ from .messages import (
from .ripemd160 import ripemd160
MAX_SCRIPT_ELEMENT_SIZE = 520
+MAX_PUBKEYS_PER_MULTI_A = 999
LOCKTIME_THRESHOLD = 500000000
ANNEX_TAG = 0x50
diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py
index 165c5b8d83..2fb9ec0942 100755
--- a/test/functional/test_framework/test_framework.py
+++ b/test/functional/test_framework/test_framework.py
@@ -448,11 +448,15 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def get_bin_from_version(version, bin_name, bin_default):
if not version:
return bin_default
+ if version > 219999:
+ # Starting at client version 220000 the first two digits represent
+ # the major version, e.g. v22.0 instead of v0.22.0.
+ version *= 100
return os.path.join(
self.options.previous_releases_path,
re.sub(
- r'\.0$',
- '', # remove trailing .0 for point releases
+ r'\.0$' if version <= 219999 else r'(\.0){1,2}$',
+ '', # Remove trailing dot for point releases, after 22.0 also remove double trailing dot.
'v{}.{}.{}.{}'.format(
(version % 100000000) // 1000000,
(version % 1000000) // 10000,
@@ -474,7 +478,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
versions = [None] * num_nodes
if self.is_syscall_sandbox_compiled() and not self.disable_syscall_sandbox:
for i in range(len(extra_args)):
- if versions[i] is None or versions[i] >= 219900:
+ # The -sandbox argument is not present in the v22.0 release.
+ if versions[i] is None or versions[i] >= 229900:
extra_args[i] = extra_args[i] + ["-sandbox=log-and-abort"]
if binary is None:
binary = [get_bin_from_version(v, 'bitcoind', self.options.bitcoind) for v in versions]
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 289e83579b..e56d4aa492 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -743,6 +743,9 @@ class RPCOverloadWrapper():
def __getattr__(self, name):
return getattr(self.rpc, name)
+ def createwallet_passthrough(self, *args, **kwargs):
+ return self.__getattr__("createwallet")(*args, **kwargs)
+
def createwallet(self, wallet_name, disable_private_keys=None, blank=None, passphrase='', avoid_reuse=None, descriptors=None, load_on_startup=None, external_signer=None):
if descriptors is None:
descriptors = self.descriptors
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index 210025104e..8651bcf636 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -573,17 +573,17 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
return txids
-def mine_large_block(test_framework, node, utxos=None):
+def mine_large_block(test_framework, mini_wallet, node):
# generate a 66k transaction,
# and 14 of them is close to the 1MB block limit
- num = 14
txouts = gen_return_txouts()
- utxos = utxos if utxos is not None else []
- if len(utxos) < num:
- utxos.clear()
- utxos.extend(node.listunspent())
- fee = 100 * node.getnetworkinfo()["relayfee"]
- create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee)
+ from .messages import COIN
+ fee = 100 * int(node.getnetworkinfo()["relayfee"] * COIN)
+ for _ in range(14):
+ tx = mini_wallet.create_self_transfer(from_node=node, fee_rate=0, mempool_valid=False)['tx']
+ tx.vout[0].nValue -= fee
+ tx.vout.extend(txouts)
+ mini_wallet.sendrawtransaction(from_node=node, tx_hex=tx.serialize().hex())
test_framework.generate(node, 1)
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index dd41a740ae..e86f365f11 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -8,7 +8,10 @@ from copy import deepcopy
from decimal import Decimal
from enum import Enum
from random import choice
-from typing import Optional
+from typing import (
+ Any,
+ Optional,
+)
from test_framework.address import (
base58_to_byte,
create_deterministic_address_bcrt1_p2tr_op_true,
@@ -93,6 +96,9 @@ class MiniWallet:
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ def get_balance(self):
+ return sum(u['value'] for u in self._utxos)
+
def rescan_utxos(self):
"""Drop all utxos and rescan the utxo set"""
self._utxos = []
@@ -131,24 +137,30 @@ class MiniWallet:
self._utxos.append({'txid': cb_tx['txid'], 'vout': 0, 'value': cb_tx['vout'][0]['value'], 'height': block_info['height']})
return blocks
+ def get_scriptPubKey(self):
+ return self._scriptPubKey
+
def get_descriptor(self):
return descsum_create(f'raw({self._scriptPubKey.hex()})')
def get_address(self):
return self._address
- def get_utxo(self, *, txid: Optional[str]='', mark_as_spent=True):
+ def get_utxo(self, *, txid: str = '', vout: Optional[int] = None, mark_as_spent=True):
"""
Returns a utxo and marks it as spent (pops it from the internal list)
Args:
txid: get the first utxo we find from a specific transaction
"""
- index = -1 # by default the last utxo
self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height'])) # Put the largest utxo last
if txid:
- utxo = next(filter(lambda utxo: txid == utxo['txid'], self._utxos))
- index = self._utxos.index(utxo)
+ utxo_filter: Any = filter(lambda utxo: txid == utxo['txid'], self._utxos)
+ else:
+ utxo_filter = reversed(self._utxos) # By default the largest utxo
+ if vout is not None:
+ utxo_filter = filter(lambda utxo: vout == utxo['vout'], utxo_filter)
+ index = self._utxos.index(next(utxo_filter))
if mark_as_spent:
return self._utxos.pop(index)
else:
@@ -179,6 +191,47 @@ class MiniWallet:
txid = self.sendrawtransaction(from_node=from_node, tx_hex=tx.serialize().hex())
return txid, 1
+ def send_self_transfer_multi(self, **kwargs):
+ """
+ Create and send a transaction that spends the given UTXOs and creates a
+ certain number of outputs with equal amounts.
+
+ Returns a dictionary with
+ - txid
+ - serialized transaction in hex format
+ - transaction as CTransaction instance
+ - list of newly created UTXOs, ordered by vout index
+ """
+ tx = self.create_self_transfer_multi(**kwargs)
+ txid = self.sendrawtransaction(from_node=kwargs['from_node'], tx_hex=tx.serialize().hex())
+ return {'new_utxos': [self.get_utxo(txid=txid, vout=vout) for vout in range(len(tx.vout))],
+ 'txid': txid, 'hex': tx.serialize().hex(), 'tx': tx}
+
+ def create_self_transfer_multi(self, *, from_node, utxos_to_spend=None, num_outputs=1, fee_per_output=1000):
+ """
+ Create and return a transaction that spends the given UTXOs and creates a
+ certain number of outputs with equal amounts.
+ """
+ utxos_to_spend = utxos_to_spend or [self.get_utxo()]
+ # create simple tx template (1 input, 1 output)
+ tx = self.create_self_transfer(fee_rate=0, from_node=from_node, utxo_to_spend=utxos_to_spend[0], mempool_valid=False)['tx']
+
+ # duplicate inputs, witnesses and outputs
+ tx.vin = [deepcopy(tx.vin[0]) for _ in range(len(utxos_to_spend))]
+ tx.wit.vtxinwit = [deepcopy(tx.wit.vtxinwit[0]) for _ in range(len(utxos_to_spend))]
+ tx.vout = [deepcopy(tx.vout[0]) for _ in range(num_outputs)]
+
+ # adapt input prevouts
+ for i, utxo in enumerate(utxos_to_spend):
+ tx.vin[i] = CTxIn(COutPoint(int(utxo['txid'], 16), utxo['vout']))
+
+ # adapt output amounts (use fixed fee per output)
+ inputs_value_total = sum([int(COIN * utxo['value']) for utxo in utxos_to_spend])
+ outputs_value_total = inputs_value_total - fee_per_output * num_outputs
+ for o in tx.vout:
+ o.nValue = outputs_value_total // num_outputs
+ return tx
+
def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node=None, utxo_to_spend=None, mempool_valid=True, locktime=0, sequence=0):
"""Create and return a tx with the specified fee_rate. Fee may be exact or at most one satoshi higher than needed."""
from_node = from_node or self._test_node
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 701c683180..1f0f806d91 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -191,8 +191,7 @@ BASE_SCRIPTS = [
'rpc_decodescript.py',
'rpc_blockchain.py',
'rpc_deprecated.py',
- 'wallet_disable.py --legacy-wallet',
- 'wallet_disable.py --descriptors',
+ 'wallet_disable.py',
'p2p_addr_relay.py',
'p2p_getaddr_caching.py',
'p2p_getdata.py',
@@ -228,8 +227,7 @@ BASE_SCRIPTS = [
'feature_rbf.py --descriptors',
'mempool_packages.py',
'mempool_package_onemore.py',
- 'rpc_createmultisig.py --legacy-wallet',
- 'rpc_createmultisig.py --descriptors',
+ 'rpc_createmultisig.py',
'rpc_packages.py',
'mempool_package_limits.py',
'feature_versionbits_warning.py',
@@ -240,7 +238,6 @@ BASE_SCRIPTS = [
'p2p_eviction.py',
'wallet_signmessagewithaddress.py',
'rpc_signmessagewithprivkey.py',
- 'rpc_generateblock.py',
'rpc_generate.py',
'wallet_balance.py --legacy-wallet',
'wallet_balance.py --descriptors',
@@ -279,11 +276,15 @@ BASE_SCRIPTS = [
'feature_minchainwork.py',
'rpc_estimatefee.py',
'rpc_getblockstats.py',
+ 'feature_bind_port_externalip.py',
'wallet_create_tx.py --legacy-wallet',
'wallet_send.py --legacy-wallet',
'wallet_send.py --descriptors',
+ 'wallet_sendall.py --legacy-wallet',
+ 'wallet_sendall.py --descriptors',
'wallet_create_tx.py --descriptors',
'wallet_taproot.py',
+ 'wallet_inactive_hdchains.py',
'p2p_fingerprint.py',
'feature_uacomment.py',
'feature_init.py',
@@ -293,6 +294,7 @@ BASE_SCRIPTS = [
'feature_loadblock.py',
'p2p_dos_header_tree.py',
'p2p_add_connections.py',
+ 'feature_bind_port_discover.py',
'p2p_unrequested_blocks.py',
'p2p_blockfilters.py',
'p2p_message_capture.py',
@@ -307,10 +309,10 @@ BASE_SCRIPTS = [
'p2p_ping.py',
'rpc_scantxoutset.py',
'feature_txindex_compatibility.py',
+ 'feature_unsupported_utxo_db.py',
'feature_logging.py',
'feature_anchors.py',
- 'feature_coinstatsindex.py --legacy-wallet',
- 'feature_coinstatsindex.py --descriptors',
+ 'feature_coinstatsindex.py',
'wallet_orphanedreward.py',
'wallet_timelock.py',
'p2p_node_network_limited.py',
@@ -589,11 +591,12 @@ def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=
# Clean up dangling processes if any. This may only happen with --failfast option.
# Killing the process group will also terminate the current process but that is
# not an issue
- if len(job_queue.jobs):
+ if not os.getenv("CI_FAILFAST_TEST_LEAVE_DANGLING") and len(job_queue.jobs):
os.killpg(os.getpgid(0), signal.SIGKILL)
sys.exit(not all_passed)
+
def print_results(test_results, max_len_name, runtime):
results = "\n" + BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "STATUS ", "DURATION") + BOLD[0]
diff --git a/test/functional/wallet_balance.py b/test/functional/wallet_balance.py
index 0cfbefb719..0c93821e7f 100755
--- a/test/functional/wallet_balance.py
+++ b/test/functional/wallet_balance.py
@@ -50,7 +50,9 @@ class WalletTest(BitcoinTestFramework):
self.num_nodes = 2
self.setup_clean_chain = True
self.extra_args = [
- ['-limitdescendantcount=3'], # Limit mempool descendants as a hack to have wallet txs rejected from the mempool
+ # Limit mempool descendants as a hack to have wallet txs rejected from the mempool.
+ # Set walletrejectlongchains=0 so the wallet still creates the transactions.
+ ['-limitdescendantcount=3', '-walletrejectlongchains=0'],
[],
]
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 69f9df57d8..a6c93ba5f9 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -25,7 +25,7 @@ class WalletTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.extra_args = [[
- "-acceptnonstdtxn=1",
+ "-acceptnonstdtxn=1", "-walletrejectlongchains=0"
]] * self.num_nodes
self.setup_clean_chain = True
self.supports_cli = False
@@ -142,7 +142,7 @@ class WalletTest(BitcoinTestFramework):
self.nodes[2].lockunspent(False, [unspent_0], True)
# Restarting the node with the lock written to the wallet should keep the lock
- self.restart_node(2)
+ self.restart_node(2, ["-walletrejectlongchains=0"])
assert_raises_rpc_error(-8, "Invalid parameter, output already locked", self.nodes[2].lockunspent, False, [unspent_0])
# Unloading and reloading the wallet with a persistent lock should keep the lock
@@ -568,7 +568,7 @@ class WalletTest(BitcoinTestFramework):
self.log.info("Test -reindex")
self.stop_nodes()
# set lower ancestor limit for later
- self.start_node(0, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
+ self.start_node(0, ['-reindex', "-walletrejectlongchains=0", "-limitancestorcount=" + str(chainlimit)])
self.start_node(1, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
self.start_node(2, ['-reindex', "-limitancestorcount=" + str(chainlimit)])
# reindex will leave rpc warm up "early"; Wait for it to finish
@@ -668,7 +668,7 @@ class WalletTest(BitcoinTestFramework):
"category": baz["category"],
"vout": baz["vout"]}
expected_fields = frozenset({'amount', 'bip125-replaceable', 'confirmations', 'details', 'fee',
- 'hex', 'time', 'timereceived', 'trusted', 'txid', 'walletconflicts'})
+ 'hex', 'time', 'timereceived', 'trusted', 'txid', 'wtxid', 'walletconflicts'})
verbose_field = "decoded"
expected_verbose_fields = expected_fields | {verbose_field}
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index f6843d597d..3b23ee8e94 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -442,7 +442,9 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):
self.generate(peer_node, 1)
# Create single-input PSBT for transaction to be bumped
- psbt = watcher.walletcreatefundedpsbt([], {dest_address: 0.0005}, 0, {"fee_rate": 1}, True)['psbt']
+ # Ensure the payment amount + change can be fully funded using one of the 0.001BTC inputs.
+ psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: 0.0005}, 0,
+ {"fee_rate": 1, "add_inputs": False}, True)['psbt']
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
psbt_final = watcher.finalizepsbt(psbt_signed["psbt"])
original_txid = watcher.sendrawtransaction(psbt_final["hex"])
diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py
index 4416a9655f..e8234de032 100755
--- a/test/functional/wallet_createwallet.py
+++ b/test/functional/wallet_createwallet.py
@@ -164,5 +164,10 @@ class CreateWalletTest(BitcoinTestFramework):
self.log.info('Using a passphrase with private keys disabled returns error')
assert_raises_rpc_error(-4, 'Passphrase provided but private keys are disabled. A passphrase is only used to encrypt private keys, so cannot be used for wallets with private keys disabled.', self.nodes[0].createwallet, wallet_name='w9', disable_private_keys=True, passphrase='thisisapassphrase')
+ if self.is_bdb_compiled():
+ self.log.info("Test legacy wallet deprecation")
+ res = self.nodes[0].createwallet(wallet_name="legacy_w0", descriptors=False, passphrase=None)
+ assert_equal(res["warning"], "Wallet created successfully. The legacy wallet type is being deprecated and support for creating and opening legacy wallets will be removed in the future.")
+
if __name__ == '__main__':
CreateWalletTest().main()
diff --git a/test/functional/wallet_disable.py b/test/functional/wallet_disable.py
index 2c7996ca6b..74cddf2738 100755
--- a/test/functional/wallet_disable.py
+++ b/test/functional/wallet_disable.py
@@ -26,10 +26,6 @@ class DisableWalletTest (BitcoinTestFramework):
x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
assert x['isvalid'] == True
- # Checking mining to an address without a wallet. Generating to a valid address should succeed
- # but generating to an invalid address will fail.
- self.generatetoaddress(self.nodes[0], 1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ')
- assert_raises_rpc_error(-5, "Invalid address", self.generatetoaddress, self.nodes[0], 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy')
if __name__ == '__main__':
- DisableWalletTest ().main ()
+ DisableWalletTest().main()
diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py
index 0d702e44f6..0c9106f800 100755
--- a/test/functional/wallet_encryption.py
+++ b/test/functional/wallet_encryption.py
@@ -35,14 +35,14 @@ class WalletEncryptionTest(BitcoinTestFramework):
assert_raises_rpc_error(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.", self.nodes[0].walletpassphrasechange, 'ff', 'ff')
# Encrypt the wallet
- assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].encryptwallet, '')
+ assert_raises_rpc_error(-8, "passphrase cannot be empty", self.nodes[0].encryptwallet, '')
self.nodes[0].encryptwallet(passphrase)
# Test that the wallet is encrypted
assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signmessage, address, msg)
assert_raises_rpc_error(-15, "Error: running with an encrypted wallet, but encryptwallet was called.", self.nodes[0].encryptwallet, 'ff')
- assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].walletpassphrase, '', 1)
- assert_raises_rpc_error(-8, "passphrase can not be empty", self.nodes[0].walletpassphrasechange, '', 'ff')
+ assert_raises_rpc_error(-8, "passphrase cannot be empty", self.nodes[0].walletpassphrase, '', 1)
+ assert_raises_rpc_error(-8, "passphrase cannot be empty", self.nodes[0].walletpassphrasechange, '', 'ff')
# Check that walletpassphrase works
self.nodes[0].walletpassphrase(passphrase, 2)
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index cdb5823109..2a4d0981c7 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -5,9 +5,13 @@
"""Test the importprunedfunds and removeprunedfunds RPCs."""
from decimal import Decimal
-from test_framework.blocktools import COINBASE_MATURITY
from test_framework.address import key_to_p2wpkh
+from test_framework.blocktools import COINBASE_MATURITY
from test_framework.key import ECKey
+from test_framework.messages import (
+ CMerkleBlock,
+ from_hex,
+)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
@@ -15,6 +19,7 @@ from test_framework.util import (
)
from test_framework.wallet_util import bytes_to_wif
+
class ImportPrunedFundsTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@@ -124,5 +129,18 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
w1.removeprunedfunds(txnid3)
assert not [tx for tx in w1.listtransactions(include_watchonly=True) if tx['txid'] == txnid3]
+ # Check various RPC parameter validation errors
+ assert_raises_rpc_error(-22, "TX decode failed", w1.importprunedfunds, b'invalid tx'.hex(), proof1)
+ assert_raises_rpc_error(-5, "Transaction given doesn't exist in proof", w1.importprunedfunds, rawtxn2, proof1)
+
+ mb = from_hex(CMerkleBlock(), proof1)
+ mb.header.hashMerkleRoot = 0xdeadbeef # cause mismatch between merkle root and merkle block
+ assert_raises_rpc_error(-5, "Something wrong with merkleblock", w1.importprunedfunds, rawtxn1, mb.serialize().hex())
+
+ mb = from_hex(CMerkleBlock(), proof1)
+ mb.header.nTime += 1 # modify arbitrary block header field to change block hash
+ assert_raises_rpc_error(-5, "Block not found in chain", w1.importprunedfunds, rawtxn1, mb.serialize().hex())
+
+
if __name__ == '__main__':
ImportPrunedFundsTest().main()
diff --git a/test/functional/wallet_inactive_hdchains.py b/test/functional/wallet_inactive_hdchains.py
new file mode 100755
index 0000000000..e1dad00876
--- /dev/null
+++ b/test/functional/wallet_inactive_hdchains.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""
+Test Inactive HD Chains.
+"""
+import os
+import shutil
+import time
+
+from test_framework.authproxy import JSONRPCException
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.wallet_util import (
+ get_generate_key,
+)
+
+
+class InactiveHDChainsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 2
+ self.extra_args = [["-keypool=10"], ["-nowallet", "-keypool=10"]]
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+ self.skip_if_no_bdb()
+ self.skip_if_no_previous_releases()
+
+ def setup_nodes(self):
+ self.add_nodes(self.num_nodes, extra_args=self.extra_args, versions=[
+ None,
+ 170200, # 0.17.2 Does not have the key metadata upgrade
+ ])
+
+ self.start_nodes()
+ self.init_wallet(node=0)
+
+ def prepare_wallets(self, wallet_basename, encrypt=False):
+ self.nodes[0].createwallet(wallet_name=f"{wallet_basename}_base", descriptors=False, blank=True)
+ self.nodes[0].createwallet(wallet_name=f"{wallet_basename}_test", descriptors=False, blank=True)
+ base_wallet = self.nodes[0].get_wallet_rpc(f"{wallet_basename}_base")
+ test_wallet = self.nodes[0].get_wallet_rpc(f"{wallet_basename}_test")
+
+ # Setup both wallets with the same HD seed
+ seed = get_generate_key()
+ base_wallet.sethdseed(True, seed.privkey)
+ test_wallet.sethdseed(True, seed.privkey)
+
+ if encrypt:
+ # Encrypting will generate a new HD seed and flush the keypool
+ test_wallet.encryptwallet("pass")
+ else:
+ # Generate a new HD seed on the test wallet
+ test_wallet.sethdseed()
+
+ return base_wallet, test_wallet
+
+ def do_inactive_test(self, base_wallet, test_wallet, encrypt=False):
+ default = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+
+ # The first address should be known by both wallets.
+ addr1 = base_wallet.getnewaddress()
+ assert test_wallet.getaddressinfo(addr1)["ismine"]
+ # The address at index 9 is the first address that the test wallet will not know initially
+ for _ in range(0, 9):
+ base_wallet.getnewaddress()
+ addr2 = base_wallet.getnewaddress()
+ assert not test_wallet.getaddressinfo(addr2)["ismine"]
+
+ # Send to first address on the old seed
+ txid = default.sendtoaddress(addr1, 10)
+ self.generate(self.nodes[0], 1)
+
+ # Wait for the test wallet to see the transaction
+ while True:
+ try:
+ test_wallet.gettransaction(txid)
+ break
+ except JSONRPCException:
+ time.sleep(0.1)
+
+ if encrypt:
+ # The test wallet will not be able to generate the topped up keypool
+ # until it is unlocked. So it still should not know about the second address
+ assert not test_wallet.getaddressinfo(addr2)["ismine"]
+ test_wallet.walletpassphrase("pass", 1)
+
+ # The test wallet should now know about the second address as it
+ # should have generated it in the inactive chain's keypool
+ assert test_wallet.getaddressinfo(addr2)["ismine"]
+
+ # Send to second address on the old seed
+ txid = default.sendtoaddress(addr2, 10)
+ self.generate(self.nodes[0], 1)
+ test_wallet.gettransaction(txid)
+
+ def test_basic(self):
+ self.log.info("Test basic case for inactive HD chains")
+ self.do_inactive_test(*self.prepare_wallets("basic"))
+
+ def test_encrypted_wallet(self):
+ self.log.info("Test inactive HD chains when wallet is encrypted")
+ self.do_inactive_test(*self.prepare_wallets("enc", encrypt=True), encrypt=True)
+
+ def test_without_upgraded_keymeta(self):
+ # Test that it is possible to top up inactive hd chains even if there is no key origin
+ # in CKeyMetadata. This tests for the segfault reported in
+ # https://github.com/bitcoin/bitcoin/issues/21605
+ self.log.info("Test that topping up inactive HD chains does not need upgraded key origin")
+
+ self.nodes[0].createwallet(wallet_name="keymeta_base", descriptors=False, blank=True)
+ # Createwallet is overridden in the test framework so that the descriptor option can be filled
+ # depending on the test's cli args. However we don't want to do that when using old nodes that
+ # do not support descriptors. So we use the createwallet_passthrough function.
+ self.nodes[1].createwallet_passthrough(wallet_name="keymeta_test")
+ base_wallet = self.nodes[0].get_wallet_rpc("keymeta_base")
+ test_wallet = self.nodes[1].get_wallet_rpc("keymeta_test")
+
+ # Setup both wallets with the same HD seed
+ seed = get_generate_key()
+ base_wallet.sethdseed(True, seed.privkey)
+ test_wallet.sethdseed(True, seed.privkey)
+
+ # Encrypting will generate a new HD seed and flush the keypool
+ test_wallet.encryptwallet("pass")
+
+ # Copy test wallet to node 0
+ test_wallet.unloadwallet()
+ test_wallet_dir = os.path.join(self.nodes[1].datadir, "regtest/wallets/keymeta_test")
+ new_test_wallet_dir = os.path.join(self.nodes[0].datadir, "regtest/wallets/keymeta_test")
+ shutil.copytree(test_wallet_dir, new_test_wallet_dir)
+ self.nodes[0].loadwallet("keymeta_test")
+ test_wallet = self.nodes[0].get_wallet_rpc("keymeta_test")
+
+ self.do_inactive_test(base_wallet, test_wallet, encrypt=True)
+
+ def run_test(self):
+ self.generate(self.nodes[0], 101)
+
+ self.test_basic()
+ self.test_encrypted_wallet()
+ self.test_without_upgraded_keymeta()
+
+
+if __name__ == '__main__':
+ InactiveHDChainsTest().main()
diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py
index 5aae2c813a..6552bfe60c 100755
--- a/test/functional/wallet_resendwallettransactions.py
+++ b/test/functional/wallet_resendwallettransactions.py
@@ -38,7 +38,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework):
# Can take a few seconds due to transaction trickling
peer_first.wait_for_broadcast([txid])
- # Add a second peer since txs aren't rebroadcast to the same peer (see filterInventoryKnown)
+ # Add a second peer since txs aren't rebroadcast to the same peer (see m_tx_inventory_known_filter)
peer_second = node.add_p2p_connection(P2PTxInvStore())
self.log.info("Create a block")
diff --git a/test/functional/wallet_sendall.py b/test/functional/wallet_sendall.py
new file mode 100755
index 0000000000..aa8d2a9d2c
--- /dev/null
+++ b/test/functional/wallet_sendall.py
@@ -0,0 +1,316 @@
+#!/usr/bin/env python3
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test the sendall RPC command."""
+
+from decimal import Decimal, getcontext
+
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+ assert_greater_than,
+ assert_raises_rpc_error,
+)
+
+# Decorator to reset activewallet to zero utxos
+def cleanup(func):
+ def wrapper(self):
+ try:
+ func(self)
+ finally:
+ if 0 < self.wallet.getbalances()["mine"]["trusted"]:
+ self.wallet.sendall([self.remainder_target])
+ assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty
+ return wrapper
+
+class SendallTest(BitcoinTestFramework):
+ # Setup and helpers
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def set_test_params(self):
+ getcontext().prec=10
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def assert_balance_swept_completely(self, tx, balance):
+ output_sum = sum([o["value"] for o in tx["decoded"]["vout"]])
+ assert_equal(output_sum, balance + tx["fee"])
+ assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty
+
+ def assert_tx_has_output(self, tx, addr, value=None):
+ for output in tx["decoded"]["vout"]:
+ if addr == output["scriptPubKey"]["address"] and value is None or value == output["value"]:
+ return
+ raise AssertionError("Output to {} not present or wrong amount".format(addr))
+
+ def assert_tx_has_outputs(self, tx, expected_outputs):
+ assert_equal(len(expected_outputs), len(tx["decoded"]["vout"]))
+ for eo in expected_outputs:
+ self.assert_tx_has_output(tx, eo["address"], eo["value"])
+
+ def add_utxos(self, amounts):
+ for a in amounts:
+ self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), a)
+ self.generate(self.nodes[0], 1)
+ assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 0)
+ return self.wallet.getbalances()["mine"]["trusted"]
+
+ # Helper schema for success cases
+ def test_sendall_success(self, sendall_args, remaining_balance = 0):
+ sendall_tx_receipt = self.wallet.sendall(sendall_args)
+ self.generate(self.nodes[0], 1)
+ # wallet has remaining balance (usually empty)
+ assert_equal(remaining_balance, self.wallet.getbalances()["mine"]["trusted"])
+
+ assert_equal(sendall_tx_receipt["complete"], True)
+ return self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)
+
+ @cleanup
+ def gen_and_clean(self):
+ self.add_utxos([15, 2, 4])
+
+ def test_cleanup(self):
+ self.log.info("Test that cleanup wrapper empties wallet")
+ self.gen_and_clean()
+ assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty
+
+ # Actual tests
+ @cleanup
+ def sendall_two_utxos(self):
+ self.log.info("Testing basic sendall case without specific amounts")
+ pre_sendall_balance = self.add_utxos([10,11])
+ tx_from_wallet = self.test_sendall_success(sendall_args = [self.remainder_target])
+
+ self.assert_tx_has_outputs(tx = tx_from_wallet,
+ expected_outputs = [
+ { "address": self.remainder_target, "value": pre_sendall_balance + tx_from_wallet["fee"] } # fee is neg
+ ]
+ )
+ self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)
+
+ @cleanup
+ def sendall_split(self):
+ self.log.info("Testing sendall where two recipients have unspecified amount")
+ pre_sendall_balance = self.add_utxos([1, 2, 3, 15])
+ tx_from_wallet = self.test_sendall_success([self.remainder_target, self.split_target])
+
+ half = (pre_sendall_balance + tx_from_wallet["fee"]) / 2
+ self.assert_tx_has_outputs(tx_from_wallet,
+ expected_outputs = [
+ { "address": self.split_target, "value": half },
+ { "address": self.remainder_target, "value": half }
+ ]
+ )
+ self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)
+
+ @cleanup
+ def sendall_and_spend(self):
+ self.log.info("Testing sendall in combination with paying specified amount to recipient")
+ pre_sendall_balance = self.add_utxos([8, 13])
+ tx_from_wallet = self.test_sendall_success([{self.recipient: 5}, self.remainder_target])
+
+ self.assert_tx_has_outputs(tx_from_wallet,
+ expected_outputs = [
+ { "address": self.recipient, "value": 5 },
+ { "address": self.remainder_target, "value": pre_sendall_balance - 5 + tx_from_wallet["fee"] }
+ ]
+ )
+ self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)
+
+ @cleanup
+ def sendall_invalid_recipient_addresses(self):
+ self.log.info("Test having only recipient with specified amount, missing recipient with unspecified amount")
+ self.add_utxos([12, 9])
+
+ assert_raises_rpc_error(
+ -8,
+ "Must provide at least one address without a specified amount" ,
+ self.wallet.sendall,
+ [{self.recipient: 5}]
+ )
+
+ @cleanup
+ def sendall_duplicate_recipient(self):
+ self.log.info("Test duplicate destination")
+ self.add_utxos([1, 8, 3, 9])
+
+ assert_raises_rpc_error(
+ -8,
+ "Invalid parameter, duplicated address: {}".format(self.remainder_target),
+ self.wallet.sendall,
+ [self.remainder_target, self.remainder_target]
+ )
+
+ @cleanup
+ def sendall_invalid_amounts(self):
+ self.log.info("Test sending more than balance")
+ pre_sendall_balance = self.add_utxos([7, 14])
+
+ expected_tx = self.wallet.sendall(recipients=[{self.recipient: 5}, self.remainder_target], options={"add_to_wallet": False})
+ tx = self.wallet.decoderawtransaction(expected_tx['hex'])
+ fee = 21 - sum([o["value"] for o in tx["vout"]])
+
+ assert_raises_rpc_error(-6, "Assigned more value to outputs than available funds.", self.wallet.sendall,
+ [{self.recipient: pre_sendall_balance + 1}, self.remainder_target])
+ assert_raises_rpc_error(-6, "Insufficient funds for fees after creating specified outputs.", self.wallet.sendall,
+ [{self.recipient: pre_sendall_balance}, self.remainder_target])
+ assert_raises_rpc_error(-8, "Specified output amount to {} is below dust threshold".format(self.recipient),
+ self.wallet.sendall, [{self.recipient: 0.00000001}, self.remainder_target])
+ assert_raises_rpc_error(-6, "Dynamically assigned remainder results in dust output.", self.wallet.sendall,
+ [{self.recipient: pre_sendall_balance - fee}, self.remainder_target])
+ assert_raises_rpc_error(-6, "Dynamically assigned remainder results in dust output.", self.wallet.sendall,
+ [{self.recipient: pre_sendall_balance - fee - Decimal(0.00000010)}, self.remainder_target])
+
+ # @cleanup not needed because different wallet used
+ def sendall_negative_effective_value(self):
+ self.log.info("Test that sendall fails if all UTXOs have negative effective value")
+ # Use dedicated wallet for dust amounts and unload wallet at end
+ self.nodes[0].createwallet("dustwallet")
+ dust_wallet = self.nodes[0].get_wallet_rpc("dustwallet")
+
+ self.def_wallet.sendtoaddress(dust_wallet.getnewaddress(), 0.00000400)
+ self.def_wallet.sendtoaddress(dust_wallet.getnewaddress(), 0.00000300)
+ self.generate(self.nodes[0], 1)
+ assert_greater_than(dust_wallet.getbalances()["mine"]["trusted"], 0)
+
+ assert_raises_rpc_error(-6, "Total value of UTXO pool too low to pay for transaction."
+ + " Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",
+ dust_wallet.sendall, recipients=[self.remainder_target], fee_rate=300)
+
+ dust_wallet.unloadwallet()
+
+ @cleanup
+ def sendall_with_send_max(self):
+ self.log.info("Check that `send_max` option causes negative value UTXOs to be left behind")
+ self.add_utxos([0.00000400, 0.00000300, 1])
+
+ # sendall with send_max
+ sendall_tx_receipt = self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, options={"send_max": True})
+ tx_from_wallet = self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)
+
+ assert_equal(len(tx_from_wallet["decoded"]["vin"]), 1)
+ self.assert_tx_has_outputs(tx_from_wallet, [{"address": self.remainder_target, "value": 1 + tx_from_wallet["fee"]}])
+ assert_equal(self.wallet.getbalances()["mine"]["trusted"], Decimal("0.00000700"))
+
+ self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 1)
+ self.generate(self.nodes[0], 1)
+
+ @cleanup
+ def sendall_specific_inputs(self):
+ self.log.info("Test sendall with a subset of UTXO pool")
+ self.add_utxos([17, 4])
+ utxo = self.wallet.listunspent()[0]
+
+ sendall_tx_receipt = self.wallet.sendall(recipients=[self.remainder_target], options={"inputs": [utxo]})
+ tx_from_wallet = self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)
+ assert_equal(len(tx_from_wallet["decoded"]["vin"]), 1)
+ assert_equal(len(tx_from_wallet["decoded"]["vout"]), 1)
+ assert_equal(tx_from_wallet["decoded"]["vin"][0]["txid"], utxo["txid"])
+ assert_equal(tx_from_wallet["decoded"]["vin"][0]["vout"], utxo["vout"])
+ self.assert_tx_has_output(tx_from_wallet, self.remainder_target)
+
+ self.generate(self.nodes[0], 1)
+ assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 0)
+
+ @cleanup
+ def sendall_fails_on_missing_input(self):
+ # fails because UTXO was previously spent, and wallet is empty
+ self.log.info("Test sendall fails because specified UTXO is not available")
+ self.add_utxos([16, 5])
+ spent_utxo = self.wallet.listunspent()[0]
+
+ # fails on unconfirmed spent UTXO
+ self.wallet.sendall(recipients=[self.remainder_target])
+ assert_raises_rpc_error(-8,
+ "Input not available. UTXO ({}:{}) was already spent.".format(spent_utxo["txid"], spent_utxo["vout"]),
+ self.wallet.sendall, recipients=[self.remainder_target], options={"inputs": [spent_utxo]})
+
+ # fails on specific previously spent UTXO, while other UTXOs exist
+ self.generate(self.nodes[0], 1)
+ self.add_utxos([19, 2])
+ assert_raises_rpc_error(-8,
+ "Input not available. UTXO ({}:{}) was already spent.".format(spent_utxo["txid"], spent_utxo["vout"]),
+ self.wallet.sendall, recipients=[self.remainder_target], options={"inputs": [spent_utxo]})
+
+ # fails because UTXO is unknown, while other UTXOs exist
+ foreign_utxo = self.def_wallet.listunspent()[0]
+ assert_raises_rpc_error(-8, "Input not found. UTXO ({}:{}) is not part of wallet.".format(foreign_utxo["txid"],
+ foreign_utxo["vout"]), self.wallet.sendall, recipients=[self.remainder_target],
+ options={"inputs": [foreign_utxo]})
+
+ @cleanup
+ def sendall_fails_on_no_address(self):
+ self.log.info("Test sendall fails because no address is provided")
+ self.add_utxos([19, 2])
+
+ assert_raises_rpc_error(
+ -8,
+ "Must provide at least one address without a specified amount" ,
+ self.wallet.sendall,
+ []
+ )
+
+ @cleanup
+ def sendall_fails_on_specific_inputs_with_send_max(self):
+ self.log.info("Test sendall fails because send_max is used while specific inputs are provided")
+ self.add_utxos([15, 6])
+ utxo = self.wallet.listunspent()[0]
+
+ assert_raises_rpc_error(-8,
+ "Cannot combine send_max with specific inputs.",
+ self.wallet.sendall,
+ recipients=[self.remainder_target],
+ options={"inputs": [utxo], "send_max": True})
+
+ def run_test(self):
+ self.nodes[0].createwallet("activewallet")
+ self.wallet = self.nodes[0].get_wallet_rpc("activewallet")
+ self.def_wallet = self.nodes[0].get_wallet_rpc(self.default_wallet_name)
+ self.generate(self.nodes[0], 101)
+ self.recipient = self.def_wallet.getnewaddress() # payee for a specific amount
+ self.remainder_target = self.def_wallet.getnewaddress() # address that receives everything left after payments and fees
+ self.split_target = self.def_wallet.getnewaddress() # 2nd target when splitting rest
+
+ # Test cleanup
+ self.test_cleanup()
+
+ # Basic sweep: everything to one address
+ self.sendall_two_utxos()
+
+ # Split remainder to two addresses with equal amounts
+ self.sendall_split()
+
+ # Pay recipient and sweep remainder
+ self.sendall_and_spend()
+
+ # sendall fails if no recipient has unspecified amount
+ self.sendall_invalid_recipient_addresses()
+
+ # Sendall fails if same destination is provided twice
+ self.sendall_duplicate_recipient()
+
+ # Sendall fails when trying to spend more than the balance
+ self.sendall_invalid_amounts()
+
+ # Sendall fails when wallet has no economically spendable UTXOs
+ self.sendall_negative_effective_value()
+
+ # Leave dust behind if using send_max
+ self.sendall_with_send_max()
+
+ # Sendall succeeds with specific inputs
+ self.sendall_specific_inputs()
+
+ # Fails for the right reasons on missing or previously spent UTXOs
+ self.sendall_fails_on_missing_input()
+
+ # Sendall fails when no address is provided
+ self.sendall_fails_on_no_address()
+
+ # Sendall fails when using send_max while specifying inputs
+ self.sendall_fails_on_specific_inputs_with_send_max()
+
+if __name__ == '__main__':
+ SendallTest().main()
diff --git a/test/functional/wallet_signer.py b/test/functional/wallet_signer.py
index 423cfecdc0..8e4e1f5d36 100755
--- a/test/functional/wallet_signer.py
+++ b/test/functional/wallet_signer.py
@@ -194,6 +194,12 @@ class WalletSignerTest(BitcoinTestFramework):
assert(res["complete"])
assert_equal(res["hex"], mock_tx)
+ self.log.info('Test sendall using hww1')
+
+ res = hww.sendall(recipients=[{dest:0.5}, hww.getrawchangeaddress()],options={"add_to_wallet": False})
+ assert(res["complete"])
+ assert_equal(res["hex"], mock_tx)
+
# # Handle error thrown by script
# self.set_mock_result(self.nodes[4], "2")
# assert_raises_rpc_error(-1, 'Unable to parse JSON',
diff --git a/test/functional/wallet_taproot.py b/test/functional/wallet_taproot.py
index 17eab25457..d3731b135a 100755
--- a/test/functional/wallet_taproot.py
+++ b/test/functional/wallet_taproot.py
@@ -12,8 +12,11 @@ from test_framework.util import assert_equal
from test_framework.descriptors import descsum_create
from test_framework.script import (
CScript,
+ MAX_PUBKEYS_PER_MULTI_A,
OP_1,
OP_CHECKSIG,
+ OP_CHECKSIGADD,
+ OP_NUMEQUAL,
taproot_construct,
)
from test_framework.segwit_addr import encode_segwit_address
@@ -167,6 +170,17 @@ def pk(hex_key):
"""Construct a script expression for taproot_construct for pk(hex_key)."""
return (None, CScript([bytes.fromhex(hex_key), OP_CHECKSIG]))
+def multi_a(k, hex_keys, sort=False):
+ """Construct a script expression for taproot_construct for a multi_a script."""
+ xkeys = [bytes.fromhex(hex_key) for hex_key in hex_keys]
+ if sort:
+ xkeys.sort()
+ ops = [xkeys[0], OP_CHECKSIG]
+ for i in range(1, len(hex_keys)):
+ ops += [xkeys[i], OP_CHECKSIGADD]
+ ops += [k, OP_NUMEQUAL]
+ return (None, CScript(ops))
+
def compute_taproot_address(pubkey, scripts):
"""Compute the address for a taproot output with given inner key and scripts."""
tap = taproot_construct(pubkey, scripts)
@@ -194,19 +208,6 @@ class WalletTaprootTest(BitcoinTestFramework):
pass
@staticmethod
- def rand_keys(n):
- ret = []
- idxes = set()
- for _ in range(n):
- while True:
- i = random.randrange(len(KEYS))
- if not i in idxes:
- break
- idxes.add(i)
- ret.append(KEYS[i])
- return ret
-
- @staticmethod
def make_desc(pattern, privmap, keys, pub_only = False):
pat = pattern.replace("$H", H_POINT)
for i in range(len(privmap)):
@@ -275,7 +276,8 @@ class WalletTaprootTest(BitcoinTestFramework):
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
test_balance = int(self.rpc_online.getbalance() * 100000000)
ret_amnt = random.randrange(100000, test_balance)
- res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True)
+ # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends.
+ res = self.rpc_online.sendtoaddress(address=self.boring.getnewaddress(), amount=Decimal(ret_amnt) / 100000000, subtractfeefromamount=True, fee_rate=200)
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
assert(self.rpc_online.gettransaction(res)["confirmations"] > 0)
@@ -306,7 +308,8 @@ class WalletTaprootTest(BitcoinTestFramework):
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
test_balance = int(self.psbt_online.getbalance() * 100000000)
ret_amnt = random.randrange(100000, test_balance)
- psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0]})['psbt']
+ # Increase fee_rate to compensate for the wallet's inability to estimate fees for script path spends.
+ psbt = self.psbt_online.walletcreatefundedpsbt([], [{self.boring.getnewaddress(): Decimal(ret_amnt) / 100000000}], None, {"subtractFeeFromOutputs":[0], "fee_rate": 200})['psbt']
res = self.psbt_offline.walletprocesspsbt(psbt)
assert(res['complete'])
rawtx = self.nodes[0].finalizepsbt(res['psbt'])['hex']
@@ -314,8 +317,9 @@ class WalletTaprootTest(BitcoinTestFramework):
self.generatetoaddress(self.nodes[0], 1, self.boring.getnewaddress(), sync_fun=self.no_op)
assert(self.psbt_online.gettransaction(txid)['confirmations'] > 0)
- def do_test(self, comment, pattern, privmap, treefn, nkeys):
- keys = self.rand_keys(nkeys * 4)
+ def do_test(self, comment, pattern, privmap, treefn):
+ nkeys = len(privmap)
+ keys = random.sample(KEYS, nkeys * 4)
self.do_test_addr(comment, pattern, privmap, treefn, keys[0:nkeys])
self.do_test_sendtoaddress(comment, pattern, privmap, treefn, keys[0:nkeys], keys[nkeys:2*nkeys])
self.do_test_psbt(comment, pattern, privmap, treefn, keys[2*nkeys:3*nkeys], keys[3*nkeys:4*nkeys])
@@ -349,64 +353,98 @@ class WalletTaprootTest(BitcoinTestFramework):
"tr(XPRV)",
"tr($1/*)",
[True],
- lambda k1: (key(k1), []),
- 1
+ lambda k1: (key(k1), [])
)
self.do_test(
"tr(H,XPRV)",
"tr($H,pk($1/*))",
[True],
- lambda k1: (key(H_POINT), [pk(k1)]),
- 1
+ lambda k1: (key(H_POINT), [pk(k1)])
)
self.do_test(
"wpkh(XPRV)",
"wpkh($1/*)",
[True],
- None,
- 1
+ None
)
self.do_test(
"tr(XPRV,{H,{H,XPUB}})",
"tr($1/*,{pk($H),{pk($H),pk($2/*)}})",
[True, False],
- lambda k1, k2: (key(k1), [pk(H_POINT), [pk(H_POINT), pk(k2)]]),
- 2
+ lambda k1, k2: (key(k1), [pk(H_POINT), [pk(H_POINT), pk(k2)]])
)
self.do_test(
"wsh(multi(1,XPRV,XPUB))",
"wsh(multi(1,$1/*,$2/*))",
[True, False],
- None,
- 2
+ None
)
self.do_test(
"tr(XPRV,{XPUB,XPUB})",
"tr($1/*,{pk($2/*),pk($2/*)})",
[True, False],
- lambda k1, k2: (key(k1), [pk(k2), pk(k2)]),
- 2
+ lambda k1, k2: (key(k1), [pk(k2), pk(k2)])
)
self.do_test(
"tr(XPRV,{{XPUB,H},{H,XPUB}})",
"tr($1/*,{{pk($2/*),pk($H)},{pk($H),pk($2/*)}})",
[True, False],
- lambda k1, k2: (key(k1), [[pk(k2), pk(H_POINT)], [pk(H_POINT), pk(k2)]]),
- 2
+ lambda k1, k2: (key(k1), [[pk(k2), pk(H_POINT)], [pk(H_POINT), pk(k2)]])
)
self.do_test(
"tr(XPUB,{{H,{H,XPUB}},{H,{H,{H,XPRV}}}})",
"tr($1/*,{{pk($H),{pk($H),pk($2/*)}},{pk($H),{pk($H),{pk($H),pk($3/*)}}}})",
[False, False, True],
- lambda k1, k2, k3: (key(k1), [[pk(H_POINT), [pk(H_POINT), pk(k2)]], [pk(H_POINT), [pk(H_POINT), [pk(H_POINT), pk(k3)]]]]),
- 3
+ lambda k1, k2, k3: (key(k1), [[pk(H_POINT), [pk(H_POINT), pk(k2)]], [pk(H_POINT), [pk(H_POINT), [pk(H_POINT), pk(k3)]]]])
)
self.do_test(
"tr(XPRV,{XPUB,{{XPUB,{H,H}},{{H,H},XPUB}}})",
"tr($1/*,{pk($2/*),{{pk($2/*),{pk($H),pk($H)}},{{pk($H),pk($H)},pk($2/*)}}})",
[True, False],
- lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]]),
- 2
+ lambda k1, k2: (key(k1), [pk(k2), [[pk(k2), [pk(H_POINT), pk(H_POINT)]], [[pk(H_POINT), pk(H_POINT)], pk(k2)]]])
+ )
+ self.do_test(
+ "tr(H,multi_a(1,XPRV))",
+ "tr($H,multi_a(1,$1/*))",
+ [True],
+ lambda k1: (key(H_POINT), [multi_a(1, [k1])])
+ )
+ self.do_test(
+ "tr(H,sortedmulti_a(1,XPRV,XPUB))",
+ "tr($H,sortedmulti_a(1,$1/*,$2/*))",
+ [True, False],
+ lambda k1, k2: (key(H_POINT), [multi_a(1, [k1, k2], True)])
+ )
+ self.do_test(
+ "tr(H,{H,multi_a(1,XPUB,XPRV)})",
+ "tr($H,{pk($H),multi_a(1,$1/*,$2/*)})",
+ [False, True],
+ lambda k1, k2: (key(H_POINT), [pk(H_POINT), [multi_a(1, [k1, k2])]])
+ )
+ self.do_test(
+ "tr(H,sortedmulti_a(1,XPUB,XPRV,XPRV))",
+ "tr($H,sortedmulti_a(1,$1/*,$2/*,$3/*))",
+ [False, True, True],
+ lambda k1, k2, k3: (key(H_POINT), [multi_a(1, [k1, k2, k3], True)])
+ )
+ self.do_test(
+ "tr(H,multi_a(2,XPRV,XPUB,XPRV))",
+ "tr($H,multi_a(2,$1/*,$2/*,$3/*))",
+ [True, False, True],
+ lambda k1, k2, k3: (key(H_POINT), [multi_a(2, [k1, k2, k3])])
+ )
+ self.do_test(
+ "tr(XPUB,{{XPUB,{XPUB,sortedmulti_a(2,XPRV,XPUB,XPRV)}})",
+ "tr($2/*,{pk($2/*),{pk($2/*),sortedmulti_a(2,$1/*,$2/*,$3/*)}})",
+ [True, False, True],
+ lambda k1, k2, k3: (key(k2), [pk(k2), [pk(k2), multi_a(2, [k1, k2, k3], True)]])
+ )
+ rnd_pos = random.randrange(MAX_PUBKEYS_PER_MULTI_A)
+ self.do_test(
+ "tr(XPUB,multi_a(1,H...,XPRV,H...))",
+ "tr($2/*,multi_a(1" + (",$H" * rnd_pos) + ",$1/*" + (",$H" * (MAX_PUBKEYS_PER_MULTI_A - 1 - rnd_pos)) + "))",
+ [True, False],
+ lambda k1, k2: (key(k2), [multi_a(1, ([H_POINT] * rnd_pos) + [k1] + ([H_POINT] * (MAX_PUBKEYS_PER_MULTI_A - 1 - rnd_pos)))])
)
self.log.info("Sending everything back...")
diff --git a/test/functional/wallet_timelock.py b/test/functional/wallet_timelock.py
index cf233a00ef..a71cec6607 100755
--- a/test/functional/wallet_timelock.py
+++ b/test/functional/wallet_timelock.py
@@ -30,7 +30,7 @@ class WalletLocktimeTest(BitcoinTestFramework):
)
self.generate(node, 1)
- self.log.info("Check that clock can not change finality of confirmed txs")
+ self.log.info("Check that clock cannot change finality of confirmed txs")
amount_before_ad = node.getreceivedbyaddress(address)
amount_before_lb = node.getreceivedbylabel(label)
list_before_ad = node.listreceivedbyaddress(address_filter=address)
diff --git a/test/functional/wallet_upgradewallet.py b/test/functional/wallet_upgradewallet.py
index 36e72f2dd9..c452e1eafd 100755
--- a/test/functional/wallet_upgradewallet.py
+++ b/test/functional/wallet_upgradewallet.py
@@ -345,5 +345,16 @@ class UpgradeWalletTest(BitcoinTestFramework):
desc_wallet = self.nodes[0].get_wallet_rpc("desc_upgrade")
self.test_upgradewallet(desc_wallet, previous_version=169900, expected_version=169900)
+ self.log.info("Checking that descriptor wallets without privkeys do nothing, successfully")
+ self.nodes[0].createwallet(wallet_name="desc_upgrade_nopriv", descriptors=True, disable_private_keys=True)
+ desc_wallet = self.nodes[0].get_wallet_rpc("desc_upgrade_nopriv")
+ self.test_upgradewallet(desc_wallet, previous_version=169900, expected_version=169900)
+
+ if self.is_bdb_compiled():
+ self.log.info("Upgrading a wallet with private keys disabled")
+ self.nodes[0].createwallet(wallet_name="privkeys_disabled_upgrade", disable_private_keys=True, descriptors=False)
+ disabled_wallet = self.nodes[0].get_wallet_rpc("privkeys_disabled_upgrade")
+ self.test_upgradewallet(disabled_wallet, previous_version=169900, expected_version=169900)
+
if __name__ == '__main__':
UpgradeWalletTest().main()
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index 177aa74191..688ca58d7f 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -19,36 +19,35 @@ import subprocess
import sys
import hashlib
-
SHA256_SUMS = {
+ "0e2819135366f150d9906e294b61dff58fd1996ebd26c2f8e979d6c0b7a79580": "bitcoin-0.14.3-aarch64-linux-gnu.tar.gz",
+ "d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz",
+ "1b0a7408c050e3d09a8be8e21e183ef7ee570385dc41216698cc3ab392a484e7": "bitcoin-0.14.3-osx64.tar.gz",
+ "706e0472dbc933ed2757650d54cbcd780fd3829ebf8f609b32780c7eedebdbc9": "bitcoin-0.14.3-x86_64-linux-gnu.tar.gz",
+ #
"d40f18b4e43c6e6370ef7db9131f584fbb137276ec2e3dba67a4b267f81cb644": "bitcoin-0.15.2-aarch64-linux-gnu.tar.gz",
"54fb877a148a6ad189a1e1ab1ff8b11181e58ff2aaf430da55b3fd46ae549a6b": "bitcoin-0.15.2-arm-linux-gnueabihf.tar.gz",
- "2b843506c3f1af0eeca5854a920264f9a829f02d0d50328005950ddcbe88874d": "bitcoin-0.15.2-i686-pc-linux-gnu.tar.gz",
"87e9340ff3d382d543b2b69112376077f0c8b4f7450d372e83b68f5a1e22b2df": "bitcoin-0.15.2-osx64.tar.gz",
"566be44190fd76daa01f13d428939dadfb8e3daacefc8fa17f433cad28f73bd5": "bitcoin-0.15.2-x86_64-linux-gnu.tar.gz",
#
"0768c6c15caffbaca6524824c9563b42c24f70633c681c2744649158aa3fd484": "bitcoin-0.16.3-aarch64-linux-gnu.tar.gz",
"fb2818069854a6ad20ea03b28b55dbd35d8b1f7d453e90b83eace5d0098a2a87": "bitcoin-0.16.3-arm-linux-gnueabihf.tar.gz",
- "75a537844313b0a84bdb61ffcdc5c4ce19a738f7ddf71007cd2edf664efd7c37": "bitcoin-0.16.3-i686-pc-linux-gnu.tar.gz",
"78c3bff3b619a19aed575961ea43cc9e142959218835cf51aede7f0b764fc25d": "bitcoin-0.16.3-osx64.tar.gz",
"5d422a9d544742bc0df12427383f9c2517433ce7b58cf672b9a9b17c2ef51e4f": "bitcoin-0.16.3-x86_64-linux-gnu.tar.gz",
#
"5a6b35d1a348a402f2d2d6ab5aed653a1a1f13bc63aaaf51605e3501b0733b7a": "bitcoin-0.17.2-aarch64-linux-gnu.tar.gz",
"d1913a5d19c8e8da4a67d1bd5205d03c8614dfd2e02bba2fe3087476643a729e": "bitcoin-0.17.2-arm-linux-gnueabihf.tar.gz",
- "d295fc93f39bbf0fd937b730a93184899a2eb6c3a6d53f3d857cbe77ef89b98c": "bitcoin-0.17.2-i686-pc-linux-gnu.tar.gz",
"a783ba20706dbfd5b47fbedf42165fce70fbbc7d78003305d964f6b3da14887f": "bitcoin-0.17.2-osx64.tar.gz",
"943f9362b9f11130177839116f48f809d83478b4c28591d486ee9a7e35179da6": "bitcoin-0.17.2-x86_64-linux-gnu.tar.gz",
#
"88f343af72803b851c7da13874cc5525026b0b55e63e1b5e1298390c4688adc6": "bitcoin-0.18.1-aarch64-linux-gnu.tar.gz",
"cc7d483e4b20c5dabd4dcaf304965214cf4934bcc029ca99cbc9af00d3771a1f": "bitcoin-0.18.1-arm-linux-gnueabihf.tar.gz",
- "989e847b3e95fc9fedc0b109cae1b4fa43348f2f712e187a118461876af9bd16": "bitcoin-0.18.1-i686-pc-linux-gnu.tar.gz",
"b7bbcee7a7540f711b171d6981f939ca8482005fde22689bc016596d80548bb1": "bitcoin-0.18.1-osx64.tar.gz",
"425ee5ec631ae8da71ebc1c3f5c0269c627cf459379b9b030f047107a28e3ef8": "bitcoin-0.18.1-riscv64-linux-gnu.tar.gz",
"600d1db5e751fa85903e935a01a74f5cc57e1e7473c15fd3e17ed21e202cfe5a": "bitcoin-0.18.1-x86_64-linux-gnu.tar.gz",
#
"3a80431717842672df682bdb619e66523b59541483297772a7969413be3502ff": "bitcoin-0.19.1-aarch64-linux-gnu.tar.gz",
"657f28213823d240dd3324d14829702f9ad6f0710f8bdd1c379cb3c447197f48": "bitcoin-0.19.1-arm-linux-gnueabihf.tar.gz",
- "10d1e53208aa7603022f4acc084a046299ab4ccf25fe01e81b3fb6f856772589": "bitcoin-0.19.1-i686-pc-linux-gnu.tar.gz",
"1ae1b87de26487075cd2fd22e0d4ead87d969bd55c44f2f1d873ecdc6147ebb3": "bitcoin-0.19.1-osx64.tar.gz",
"aa7a9563b48aa79252c8e7b6a41c07a5441bd9f14c5e4562cc72720ea6cb0ee5": "bitcoin-0.19.1-riscv64-linux-gnu.tar.gz",
"5fcac9416e486d4960e1a946145566350ca670f9aaba99de6542080851122e4c": "bitcoin-0.19.1-x86_64-linux-gnu.tar.gz",
@@ -56,9 +55,22 @@ SHA256_SUMS = {
"60c93e3462c303eb080be7cf623f1a7684b37fd47a018ad3848bc23e13c84e1c": "bitcoin-0.20.1-aarch64-linux-gnu.tar.gz",
"55b577e0fb306fb429d4be6c9316607753e8543e5946b542d75d876a2f08654c": "bitcoin-0.20.1-arm-linux-gnueabihf.tar.gz",
"b9024dde373ea7dad707363e07ec7e265383204127539ae0c234bff3a61da0d1": "bitcoin-0.20.1-osx64.tar.gz",
- "c378d4e21109f09e8829f3591e015c66632dff2925a60b64d259be05a334c30b": "bitcoin-0.20.1-osx.dmg",
"fa71cb52ee5e0459cbf5248cdec72df27995840c796f58b304607a1ed4c165af": "bitcoin-0.20.1-riscv64-linux-gnu.tar.gz",
"376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397": "bitcoin-0.20.1-x86_64-linux-gnu.tar.gz",
+
+ "43416854330914992bbba2d0e9adf2a6fff4130be9af8ae2ef1186e743d9a3fe": "bitcoin-0.21.0-aarch64-linux-gnu.tar.gz",
+ "f028af308eda45a3c4c90f9332f96b075bf21e3495c945ebce48597151808176": "bitcoin-0.21.0-arm-linux-gnueabihf.tar.gz",
+ "695fb624fa6423f5da4f443b60763dd1d77488bfe5ef63760904a7b54e91298d": "bitcoin-0.21.0-osx64.tar.gz",
+ "f8b2adfeae021a672effbc7bd40d5c48d6b94e53b2dd660f787340bf1a52e4e9": "bitcoin-0.21.0-riscv64-linux-gnu.tar.gz",
+ "da7766775e3f9c98d7a9145429f2be8297c2672fe5b118fd3dc2411fb48e0032": "bitcoin-0.21.0-x86_64-linux-gnu.tar.gz",
+
+ "ac718fed08570a81b3587587872ad85a25173afa5f9fbbd0c03ba4d1714cfa3e": "bitcoin-22.0-aarch64-linux-gnu.tar.gz",
+ "b8713c6c5f03f5258b54e9f436e2ed6d85449aa24c2c9972f91963d413e86311": "bitcoin-22.0-arm-linux-gnueabihf.tar.gz",
+ "2744d199c3343b2d94faffdfb2c94d75a630ba27301a70e47b0ad30a7e0155e9": "bitcoin-22.0-osx64.tar.gz",
+ "2cca5f99007d060aca9d8c7cbd035dfe2f040dd8200b210ce32cdf858479f70d": "bitcoin-22.0-powerpc64-linux-gnu.tar.gz",
+ "91b1e012975c5a363b5b5fcc81b5b7495e86ff703ec8262d4b9afcfec633c30d": "bitcoin-22.0-powerpc64le-linux-gnu.tar.gz",
+ "9cc3a62c469fe57e11485fdd32c916f10ce7a2899299855a2e479256ff49ff3c": "bitcoin-22.0-riscv64-linux-gnu.tar.gz",
+ "59ebd25dd82a51638b7a6bb914586201e67db67b919b2a1ff08925a7936d1b16": "bitcoin-22.0-x86_64-linux-gnu.tar.gz",
}
diff --git a/test/lint/lint-all.sh b/test/lint/lint-all.sh
index fabc24c91b..fa37fa51c6 100755
--- a/test/lint/lint-all.sh
+++ b/test/lint/lint-all.sh
@@ -4,7 +4,7 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
-# This script runs all contrib/devtools/lint-*.sh files, and fails if any exit
+# This script runs all contrib/devtools/lint-* files, and fails if any exit
# with a non-zero status code.
# This script is intentionally locale dependent by not setting "export LC_ALL=C"
@@ -18,7 +18,7 @@ LINTALL=$(basename "${BASH_SOURCE[0]}")
EXIT_CODE=0
-for f in "${SCRIPTDIR}"/lint-*.sh; do
+for f in "${SCRIPTDIR}"/lint-*; do
if [ "$(basename "$f")" != "$LINTALL" ]; then
if ! "$f"; then
echo "^---- failure generated from $f"
diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py
index 68b795eef7..3723e0ee6a 100755
--- a/test/lint/lint-files.py
+++ b/test/lint/lint-files.py
@@ -13,6 +13,7 @@ import sys
from subprocess import check_output
from typing import Optional, NoReturn
+CMD_TOP_LEVEL = ["git", "rev-parse", "--show-toplevel"]
CMD_ALL_FILES = "git ls-files -z --full-name"
CMD_SOURCE_FILES = 'git ls-files -z --full-name -- "*.[cC][pP][pP]" "*.[hH]" "*.[pP][yY]" "*.[sS][hH]"'
CMD_SHEBANG_FILES = "git grep --full-name --line-number -I '^#!'"
@@ -184,6 +185,8 @@ def check_shebang_file_permissions() -> int:
def main() -> NoReturn:
+ root_dir = check_output(CMD_TOP_LEVEL).decode("utf8").strip()
+ os.chdir(root_dir)
failed_tests = 0
failed_tests += check_all_filenames()
failed_tests += check_source_filenames()
diff --git a/test/lint/lint-files.sh b/test/lint/lint-files.sh
deleted file mode 100755
index 86d7fc724a..0000000000
--- a/test/lint/lint-files.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C
-
-set -e
-cd "$(dirname "$0")/../.."
-test/lint/lint-files.py
diff --git a/test/lint/lint-format-strings.sh b/test/lint/lint-format-strings.sh
index d98f12b1a1..73730f16b3 100755
--- a/test/lint/lint-format-strings.sh
+++ b/test/lint/lint-format-strings.sh
@@ -29,7 +29,7 @@ FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS=(
)
EXIT_CODE=0
-if ! python3 -m doctest test/lint/lint-format-strings.py; then
+if ! python3 -m doctest "test/lint/run-lint-format-strings.py"; then
EXIT_CODE=1
fi
for S in "${FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS[@]}"; do
@@ -37,7 +37,7 @@ for S in "${FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS[@]}"; do
for MATCHING_FILE in $(git grep --full-name -l "${FUNCTION_NAME}" -- "*.c" "*.cpp" "*.h" | sort | grep -vE "^src/(leveldb|secp256k1|minisketch|tinyformat|univalue|test/fuzz/strprintf.cpp)"); do
MATCHING_FILES+=("${MATCHING_FILE}")
done
- if ! test/lint/lint-format-strings.py --skip-arguments "${SKIP_ARGUMENTS}" "${FUNCTION_NAME}" "${MATCHING_FILES[@]}"; then
+ if ! "test/lint/run-lint-format-strings.py" --skip-arguments "${SKIP_ARGUMENTS}" "${FUNCTION_NAME}" "${MATCHING_FILES[@]}"; then
EXIT_CODE=1
fi
done
diff --git a/test/lint/lint-python-dead-code.py b/test/lint/lint-python-dead-code.py
new file mode 100755
index 0000000000..b3f9394788
--- /dev/null
+++ b/test/lint/lint-python-dead-code.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""
+Find dead Python code.
+"""
+
+from subprocess import check_output, STDOUT, CalledProcessError
+
+FILES_ARGS = ['git', 'ls-files', '--', '*.py']
+
+
+def check_vulture_install():
+ try:
+ check_output(["vulture", "--version"])
+ except FileNotFoundError:
+ print("Skipping Python dead code linting since vulture is not installed. Install by running \"pip3 install vulture\"")
+ exit(0)
+
+
+def main():
+ check_vulture_install()
+
+ files = check_output(FILES_ARGS).decode("utf-8").splitlines()
+ # --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files.
+ # Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden.
+ vulture_args = ['vulture', '--min-confidence=100'] + files
+
+ try:
+ check_output(vulture_args, stderr=STDOUT)
+ except CalledProcessError as e:
+ print(e.output.decode("utf-8"), end="")
+ print("Python dead code detection found some issues")
+ exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-python-dead-code.sh b/test/lint/lint-python-dead-code.sh
deleted file mode 100755
index 247bfb310a..0000000000
--- a/test/lint/lint-python-dead-code.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-# Find dead Python code.
-
-export LC_ALL=C
-
-if ! command -v vulture > /dev/null; then
- echo "Skipping Python dead code linting since vulture is not installed. Install by running \"pip3 install vulture\""
- exit 0
-fi
-
-# --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files.
-# Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden.
-mapfile -t FILES < <(git ls-files -- "*.py")
-if ! vulture --min-confidence 100 "${FILES[@]}"; then
- echo "Python dead code detection found some issues"
- exit 1
-fi
diff --git a/test/lint/lint-spelling.py b/test/lint/lint-spelling.py
new file mode 100755
index 0000000000..5da1b243f7
--- /dev/null
+++ b/test/lint/lint-spelling.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2022 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+"""
+Warn in case of spelling errors.
+Note: Will exit successfully regardless of spelling errors.
+"""
+
+from subprocess import check_output, STDOUT, CalledProcessError
+
+IGNORE_WORDS_FILE = 'test/lint/spelling.ignore-words.txt'
+FILES_ARGS = ['git', 'ls-files', '--', ":(exclude)build-aux/m4/", ":(exclude)contrib/seeds/*.txt", ":(exclude)depends/", ":(exclude)doc/release-notes/", ":(exclude)src/leveldb/", ":(exclude)src/crc32c/", ":(exclude)src/qt/locale/", ":(exclude)src/qt/*.qrc", ":(exclude)src/secp256k1/", ":(exclude)src/minisketch/", ":(exclude)src/univalue/", ":(exclude)contrib/builder-keys/keys.txt", ":(exclude)contrib/guix/patches"]
+
+
+def check_codespell_install():
+ try:
+ check_output(["codespell", "--version"])
+ except FileNotFoundError:
+ print("Skipping spell check linting since codespell is not installed.")
+ exit(0)
+
+
+def main():
+ check_codespell_install()
+
+ files = check_output(FILES_ARGS).decode("utf-8").splitlines()
+ codespell_args = ['codespell', '--check-filenames', '--disable-colors', '--quiet-level=7', '--ignore-words={}'.format(IGNORE_WORDS_FILE)] + files
+
+ try:
+ check_output(codespell_args, stderr=STDOUT)
+ except CalledProcessError as e:
+ print(e.output.decode("utf-8"), end="")
+ print('^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in {}'.format(IGNORE_WORDS_FILE))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh
deleted file mode 100755
index b3e558b02a..0000000000
--- a/test/lint/lint-spelling.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-#
-# Copyright (c) 2018-2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#
-# Warn in case of spelling errors.
-# Note: Will exit successfully regardless of spelling errors.
-
-export LC_ALL=C
-
-if ! command -v codespell > /dev/null; then
- echo "Skipping spell check linting since codespell is not installed."
- exit 0
-fi
-
-IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt
-mapfile -t FILES < <(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/crc32c/" ":(exclude)src/qt/locale/" ":(exclude)src/qt/*.qrc" ":(exclude)src/secp256k1/" ":(exclude)src/minisketch/" ":(exclude)src/univalue/" ":(exclude)contrib/builder-keys/keys.txt" ":(exclude)contrib/guix/patches")
-if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} "${FILES[@]}"; then
- echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}"
-fi
diff --git a/test/lint/lint-format-strings.py b/test/lint/run-lint-format-strings.py
index 2870432bff..b814446125 100755
--- a/test/lint/lint-format-strings.py
+++ b/test/lint/run-lint-format-strings.py
@@ -16,14 +16,12 @@ FALSE_POSITIVES = [
("src/dbwrapper.cpp", "vsnprintf(p, limit - p, format, backup_ap)"),
("src/index/base.cpp", "FatalError(const char* fmt, const Args&... args)"),
("src/netbase.cpp", "LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args)"),
- ("src/util/system.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
+ ("src/clientversion.cpp", "strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION)"),
("src/validationinterface.cpp", "LogPrint(BCLog::VALIDATION, fmt \"\\n\", __VA_ARGS__)"),
("src/wallet/wallet.h", "WalletLogPrintf(std::string fmt, Params... parameters)"),
("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"),
("src/wallet/scriptpubkeyman.h", "WalletLogPrintf(std::string fmt, Params... parameters)"),
("src/wallet/scriptpubkeyman.h", "LogPrintf((\"%s \" + fmt).c_str(), m_storage.GetDisplayName(), parameters...)"),
- ("src/logging.h", "LogPrintf(const char* fmt, const Args&... args)"),
- ("src/wallet/scriptpubkeyman.h", "WalletLogPrintf(const std::string& fmt, const Params&... parameters)"),
]
diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/spelling.ignore-words.txt
index afdb0692d8..afdb0692d8 100644
--- a/test/lint/lint-spelling.ignore-words.txt
+++ b/test/lint/spelling.ignore-words.txt
diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan
index 26f3fdc7af..3acf575d07 100644
--- a/test/sanitizer_suppressions/tsan
+++ b/test/sanitizer_suppressions/tsan
@@ -26,9 +26,6 @@ deadlock:src/qt/test/*
deadlock:libdb
race:libzmq
-# Race in headers only Boost Test
-race:std::__1::ios_base::flags
-
# Intermittent issues
# -------------------
#
@@ -42,4 +39,4 @@ race:CZMQAbstractPublishNotifier::SendZmqMessage
race:epoll_ctl
# https://github.com/bitcoin/bitcoin/issues/23366
-race:std::__1::ios_base::width
+race:std::__1::ios_base::*
diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan
index bdaee5d191..e6cfe5f81a 100644
--- a/test/sanitizer_suppressions/ubsan
+++ b/test/sanitizer_suppressions/ubsan
@@ -51,22 +51,17 @@ unsigned-integer-overflow:crypto/
unsigned-integer-overflow:hash.cpp
unsigned-integer-overflow:policy/fees.cpp
unsigned-integer-overflow:prevector.h
-unsigned-integer-overflow:pubkey.h
unsigned-integer-overflow:script/interpreter.cpp
unsigned-integer-overflow:txmempool.cpp
-unsigned-integer-overflow:util/strencodings.cpp
-implicit-integer-sign-change:bech32.cpp
implicit-integer-sign-change:compat/stdin.cpp
implicit-integer-sign-change:compressor.h
implicit-integer-sign-change:crypto/
-implicit-integer-sign-change:key.cpp
implicit-integer-sign-change:policy/fees.cpp
implicit-integer-sign-change:prevector.h
implicit-integer-sign-change:script/bitcoinconsensus.cpp
implicit-integer-sign-change:script/interpreter.cpp
implicit-integer-sign-change:serialize.h
implicit-integer-sign-change:txmempool.cpp
-implicit-signed-integer-truncation:addrman.cpp
implicit-signed-integer-truncation:crypto/
implicit-unsigned-integer-truncation:crypto/
shift-base:arith_uint256.cpp
diff --git a/test/util/test_runner.py b/test/util/test_runner.py
index aa8fd6eee5..a7fc3b1dc1 100755
--- a/test/util/test_runner.py
+++ b/test/util/test_runner.py
@@ -93,7 +93,7 @@ def bctest(testDir, testObj, buildenv):
try:
outputData = open(os.path.join(testDir, outputFn), encoding="utf8").read()
except:
- logging.error("Output file " + outputFn + " can not be opened")
+ logging.error("Output file " + outputFn + " cannot be opened")
raise
if not outputData:
logging.error("Output data missing for " + outputFn)