diff options
Diffstat (limited to 'test')
-rwxr-xr-x | test/functional/feature_assumeutxo.py | 56 | ||||
-rwxr-xr-x | test/functional/feature_coinstatsindex.py | 6 | ||||
-rwxr-xr-x | test/functional/feature_dbcrash.py | 8 | ||||
-rwxr-xr-x | test/functional/feature_utxo_set_hash.py | 2 | ||||
-rwxr-xr-x | test/functional/rpc_blockchain.py | 12 | ||||
-rwxr-xr-x | test/functional/rpc_dumptxoutset.py | 2 | ||||
-rwxr-xr-x | test/functional/rpc_net.py | 9 | ||||
-rwxr-xr-x | test/functional/test_framework/wallet_util.py | 19 | ||||
-rwxr-xr-x | test/functional/wallet_createwallet.py | 60 | ||||
-rwxr-xr-x | test/functional/wallet_descriptor.py | 25 | ||||
-rwxr-xr-x | test/functional/wallet_dump.py | 41 | ||||
-rwxr-xr-x | test/functional/wallet_encryption.py | 22 | ||||
-rwxr-xr-x | test/functional/wallet_fundrawtransaction.py | 48 | ||||
-rwxr-xr-x | test/functional/wallet_keypool.py | 56 | ||||
-rwxr-xr-x | test/fuzz/test_runner.py | 16 | ||||
-rwxr-xr-x | test/lint/lint-shell.py | 2 |
16 files changed, 206 insertions, 178 deletions
diff --git a/test/functional/feature_assumeutxo.py b/test/functional/feature_assumeutxo.py index c900736ae9..9c265649d5 100755 --- a/test/functional/feature_assumeutxo.py +++ b/test/functional/feature_assumeutxo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2021 The Bitcoin Core developers +# Copyright (c) 2021-present 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 for assumeutxo, a means of quickly bootstrapping a node using @@ -17,10 +17,8 @@ The assumeutxo value generated and used here is committed to in Interesting test cases could be loading an assumeutxo snapshot file with: -- TODO: An invalid hash -- TODO: Valid hash but invalid snapshot file (bad coin height or truncated file or +- TODO: Valid hash but invalid snapshot file (bad coin height or bad other serialization) -- TODO: Valid snapshot file, but referencing an unknown block - TODO: Valid snapshot file, but referencing a snapshot block that turns out to be invalid, or has an invalid parent - TODO: Valid snapshot file and snapshot block, but the block is not on the @@ -40,7 +38,6 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) -import struct START_HEIGHT = 199 SNAPSHOT_BASE_HEIGHT = 299 @@ -72,30 +69,43 @@ class AssumeutxoTest(BitcoinTestFramework): valid_snapshot_contents = f.read() bad_snapshot_path = valid_snapshot_path + '.mod' + def expected_error(log_msg="", rpc_details=""): + with self.nodes[1].assert_debug_log([log_msg]): + assert_raises_rpc_error(-32603, f"Unable to load UTXO snapshot{rpc_details}", self.nodes[1].loadtxoutset, bad_snapshot_path) + self.log.info(" - snapshot file refering to a block that is not in the assumeutxo parameters") - # we can only test this with a block that is already known, as otherwise the `loadtxoutset` RPC - # would time out (waiting to see the hash in the headers chain), rather than error immediately - bad_snapshot_height = SNAPSHOT_BASE_HEIGHT - 1 - with open(bad_snapshot_path, 'wb') as f: - bad_snapshot_block_hash = self.nodes[0].getblockhash(bad_snapshot_height) - # block hash of the snapshot base is stored right at the start (first 32 bytes) - f.write(bytes.fromhex(bad_snapshot_block_hash)[::-1] + valid_snapshot_contents[32:]) - - expected_log = f"assumeutxo height in snapshot metadata not recognized ({bad_snapshot_height}) - refusing to load snapshot" - with self.nodes[1].assert_debug_log([expected_log]): - assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", self.nodes[1].loadtxoutset, bad_snapshot_path) + prev_block_hash = self.nodes[0].getblockhash(SNAPSHOT_BASE_HEIGHT - 1) + bogus_block_hash = "0" * 64 # Represents any unknown block hash + for bad_block_hash in [bogus_block_hash, prev_block_hash]: + with open(bad_snapshot_path, 'wb') as f: + # block hash of the snapshot base is stored right at the start (first 32 bytes) + f.write(bytes.fromhex(bad_block_hash)[::-1] + valid_snapshot_contents[32:]) + error_details = f", assumeutxo block hash in snapshot metadata not recognized ({bad_block_hash})" + expected_error(rpc_details=error_details) self.log.info(" - snapshot file with wrong number of coins") - valid_num_coins = struct.unpack("<I", valid_snapshot_contents[32:32 + 4])[0] + valid_num_coins = int.from_bytes(valid_snapshot_contents[32:32 + 8], "little") for off in [-1, +1]: with open(bad_snapshot_path, 'wb') as f: f.write(valid_snapshot_contents[:32]) - f.write(struct.pack("<I", valid_num_coins + off)) - f.write(valid_snapshot_contents[32 + 4:]) + f.write((valid_num_coins + off).to_bytes(8, "little")) + f.write(valid_snapshot_contents[32 + 8:]) + expected_error(log_msg=f"bad snapshot - coins left over after deserializing 298 coins" if off == -1 else f"bad snapshot format or truncated snapshot after deserializing 299 coins") + + self.log.info(" - snapshot file with alternated UTXO data") + cases = [ + [b"\xff" * 32, 0, "05030e506678f2eca8d624ffed97090ab3beadad1b51ee6e5985ba91c5720e37"], # wrong outpoint hash + [(1).to_bytes(4, "little"), 32, "7d29cfe2c1e242bc6f103878bb70cfffa8b4dac20dbd001ff6ce24b7de2d2399"], # wrong outpoint index + [b"\x81", 36, "f03939a195531f96d5dff983e294a1af62af86049fa7a19a7627246f237c03f1"], # wrong coin code VARINT((coinbase ? 1 : 0) | (height << 1)) + [b"\x83", 36, "e4577da84590fb288c0f7967e89575e1b0aa46624669640f6f5dfef028d39930"], # another wrong coin code + ] - expected_log = f"bad snapshot - coins left over after deserializing 298 coins" if off == -1 else f"bad snapshot format or truncated snapshot after deserializing 299 coins" - with self.nodes[1].assert_debug_log([expected_log]): - assert_raises_rpc_error(-32603, "Unable to load UTXO snapshot", self.nodes[1].loadtxoutset, bad_snapshot_path) + for content, offset, wrong_hash in cases: + with open(bad_snapshot_path, "wb") as f: + f.write(valid_snapshot_contents[:(32 + 8 + offset)]) + f.write(content) + f.write(valid_snapshot_contents[(32 + 8 + offset + len(content)):]) + expected_error(log_msg=f"[snapshot] bad snapshot content hash: expected 61d9c2b29a2571a5fe285fe2d8554f91f93309666fc9b8223ee96338de25ff53, got {wrong_hash}") def run_test(self): """ @@ -142,7 +152,7 @@ class AssumeutxoTest(BitcoinTestFramework): assert_equal( dump_output['txoutset_hash'], - 'ef45ccdca5898b6c2145e4581d2b88c56564dd389e4bd75a1aaf6961d3edd3c0') + '61d9c2b29a2571a5fe285fe2d8554f91f93309666fc9b8223ee96338de25ff53') assert_equal(dump_output['nchaintx'], 300) assert_equal(n0.getblockchaininfo()["blocks"], SNAPSHOT_BASE_HEIGHT) diff --git a/test/functional/feature_coinstatsindex.py b/test/functional/feature_coinstatsindex.py index 2ffb182946..d6c1567e64 100755 --- a/test/functional/feature_coinstatsindex.py +++ b/test/functional/feature_coinstatsindex.py @@ -293,11 +293,11 @@ class CoinStatsIndexTest(BitcoinTestFramework): def _test_index_rejects_hash_serialized(self): self.log.info("Test that the rpc raises if the legacy hash is passed with the index") - msg = "hash_serialized_2 hash type cannot be queried for a specific block" - assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111) + msg = "hash_serialized_3 hash type cannot be queried for a specific block" + assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_3', hash_or_height=111) for use_index in {True, False, None}: - assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111, use_index=use_index) + assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_3', hash_or_height=111, use_index=use_index) def _test_init_index_after_reorg(self): self.log.info("Test a reorg while the index is deactivated") diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index 3f94bbc9d1..afd0246209 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -85,7 +85,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): # Any of these RPC calls could throw due to node crash self.start_node(node_index) self.nodes[node_index].waitforblock(expected_tip) - utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2'] + utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_3'] return utxo_hash except Exception: # An exception here should mean the node is about to crash. @@ -130,7 +130,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): If any nodes crash while updating, we'll compare utxo hashes to ensure recovery was successful.""" - node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_2'] + node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_3'] # Retrieve all the blocks from node3 blocks = [] @@ -172,12 +172,12 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): """Verify that the utxo hash of each node matches node3. Restart any nodes that crash while querying.""" - node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_2'] + node3_utxo_hash = self.nodes[3].gettxoutsetinfo()['hash_serialized_3'] self.log.info("Verifying utxo hash matches for all nodes") for i in range(3): try: - nodei_utxo_hash = self.nodes[i].gettxoutsetinfo()['hash_serialized_2'] + nodei_utxo_hash = self.nodes[i].gettxoutsetinfo()['hash_serialized_3'] except OSError: # probably a crash on db flushing nodei_utxo_hash = self.restart_node(i, self.nodes[3].getbestblockhash()) diff --git a/test/functional/feature_utxo_set_hash.py b/test/functional/feature_utxo_set_hash.py index 0f510ced89..ce2a5ab8ac 100755 --- a/test/functional/feature_utxo_set_hash.py +++ b/test/functional/feature_utxo_set_hash.py @@ -69,7 +69,7 @@ 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'], "f9aa4fb5ffd10489b9a6994e70ccf1de8a8bfa2d5f201d9857332e9954b0855d") + assert_equal(node.gettxoutsetinfo()['hash_serialized_3'], "d1c7fec1c0623f6793839878cbe2a531eb968b50b27edd6e2a57077a5aed6094") assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "d1725b2fe3ef43e55aa4907480aea98d406fc9e0bf8f60169e2305f1fbf5961b") def run_test(self): diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 18a0a0c6cc..53163720bb 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -340,7 +340,7 @@ class BlockchainTest(BitcoinTestFramework): assert size > 6400 assert size < 64000 assert_equal(len(res['bestblock']), 64) - assert_equal(len(res['hash_serialized_2']), 64) + assert_equal(len(res['hash_serialized_3']), 64) self.log.info("Test gettxoutsetinfo works for blockchain with just the genesis block") b1hash = node.getblockhash(1) @@ -353,7 +353,7 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(res2['txouts'], 0) assert_equal(res2['bogosize'], 0), assert_equal(res2['bestblock'], node.getblockhash(0)) - assert_equal(len(res2['hash_serialized_2']), 64) + assert_equal(len(res2['hash_serialized_3']), 64) self.log.info("Test gettxoutsetinfo returns the same result after invalidate/reconsider block") node.reconsiderblock(b1hash) @@ -365,20 +365,20 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(res, res3) self.log.info("Test gettxoutsetinfo hash_type option") - # Adding hash_type 'hash_serialized_2', which is the default, should + # Adding hash_type 'hash_serialized_3', which is the default, should # not change the result. - res4 = node.gettxoutsetinfo(hash_type='hash_serialized_2') + res4 = node.gettxoutsetinfo(hash_type='hash_serialized_3') del res4['disk_size'] assert_equal(res, res4) # hash_type none should not return a UTXO set hash. res5 = node.gettxoutsetinfo(hash_type='none') - assert 'hash_serialized_2' not in res5 + assert 'hash_serialized_3' not in res5 # hash_type muhash should return a different UTXO set hash. res6 = node.gettxoutsetinfo(hash_type='muhash') assert 'muhash' in res6 - assert res['hash_serialized_2'] != res6['muhash'] + assert res['hash_serialized_3'] != res6['muhash'] # muhash should not be returned unless requested. for r in [res, res2, res3, res4, res5]: diff --git a/test/functional/rpc_dumptxoutset.py b/test/functional/rpc_dumptxoutset.py index f378878181..1ea6cf52d1 100755 --- a/test/functional/rpc_dumptxoutset.py +++ b/test/functional/rpc_dumptxoutset.py @@ -46,7 +46,7 @@ class DumptxoutsetTest(BitcoinTestFramework): 'b1bacb602eacf5fbc9a7c2ef6eeb0d229c04e98bdf0c2ea5929012cd0eae3830') assert_equal( - out['txoutset_hash'], '1f7e3befd45dc13ae198dfbb22869a9c5c4196f8e9ef9735831af1288033f890') + out['txoutset_hash'], 'a0b7baa3bf5ccbd3279728f230d7ca0c44a76e9923fca8f32dbfd08d65ea496a') assert_equal(out['nchaintx'], 101) # Specifying a path to an existing or invalid file will fail. diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 44b86662ea..50a022fc7e 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2017-2022 The Bitcoin Core developers +# Copyright (c) 2017-present 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 RPC calls related to net. @@ -315,9 +315,11 @@ class NetTest(BitcoinTestFramework): self.log.debug("Test that adding an address with invalid port fails") assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=-1) - assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress,address="1.2.3.4", port=65536) + assert_raises_rpc_error(-1, "JSON integer out of range", self.nodes[0].addpeeraddress, address="1.2.3.4", port=65536) self.log.debug("Test that adding a valid address to the tried table succeeds") + self.addr_time = int(time.time()) + node.setmocktime(self.addr_time) assert_equal(node.addpeeraddress(address="1.2.3.4", tried=True, port=8333), {"success": True}) with node.assert_debug_log(expected_msgs=["CheckAddrman: new 0, tried 1, total 1 started"]): addrs = node.getnodeaddresses(count=0) # getnodeaddresses re-runs the addrman checks @@ -402,8 +404,7 @@ class NetTest(BitcoinTestFramework): assert_equal(result["network"], expected["network"]) assert_equal(result["source"], expected["source"]) assert_equal(result["source_network"], expected["source_network"]) - # To avoid failing on slow test runners, use a 10s vspan here. - assert_approx(result["time"], time.time(), vspan=10) + assert_equal(result["time"], self.addr_time) def check_getrawaddrman_entries(expected): """Utility to compare a getrawaddrman result with expected addrman contents""" diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py index 319f120297..44811918bf 100755 --- a/test/functional/test_framework/wallet_util.py +++ b/test/functional/test_framework/wallet_util.py @@ -122,3 +122,22 @@ def generate_keypair(compressed=True, wif=False): if wif: privkey = bytes_to_wif(privkey.get_bytes(), compressed) return privkey, pubkey + +class WalletUnlock(): + """ + A context manager for unlocking a wallet with a passphrase and automatically locking it afterward. + """ + + MAXIMUM_TIMEOUT = 999000 + + def __init__(self, wallet, passphrase, timeout=MAXIMUM_TIMEOUT): + self.wallet = wallet + self.passphrase = passphrase + self.timeout = timeout + + def __enter__(self): + self.wallet.walletpassphrase(self.passphrase, self.timeout) + + def __exit__(self, *args): + _ = args + self.wallet.walletlock() diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index eb83e11f36..8e07021e03 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -12,7 +12,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) -from test_framework.wallet_util import generate_keypair +from test_framework.wallet_util import generate_keypair, WalletUnlock EMPTY_PASSPHRASE_MSG = "Empty string given as passphrase, wallet will not be encrypted." @@ -108,24 +108,24 @@ class CreateWalletTest(BitcoinTestFramework): w4.encryptwallet('pass') assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getnewaddress) assert_raises_rpc_error(-4, "Error: This wallet has no available keys", w4.getrawchangeaddress) - # Now set a seed and it should work. Wallet should also be encrypted - w4.walletpassphrase("pass", 999000) - if self.options.descriptors: - w4.importdescriptors([{ - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/0h/*)'), - 'timestamp': 'now', - 'active': True - }, - { - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/1h/*)'), - 'timestamp': 'now', - 'active': True, - 'internal': True - }]) - else: - w4.sethdseed() - w4.getnewaddress() - w4.getrawchangeaddress() + with WalletUnlock(w4, "pass"): + # Now set a seed and it should work. Wallet should also be encrypted + if self.options.descriptors: + w4.importdescriptors([{ + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/0h/*)'), + 'timestamp': 'now', + 'active': True + }, + { + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPcwuZGKp8TeWppSuLMiLe2d9PupB14QpPeQsqoj3LneJLhGHH13xESfvASyd4EFLJvLrG8b7DrLxEuV7hpF9uUc6XruKA1Wq/1h/*)'), + 'timestamp': 'now', + 'active': True, + 'internal': True + }]) + else: + w4.sethdseed() + w4.getnewaddress() + w4.getrawchangeaddress() self.log.info("Test blank creation with privkeys disabled and then encryption") self.nodes[0].createwallet(wallet_name='w5', disable_private_keys=True, blank=True) @@ -142,23 +142,23 @@ class CreateWalletTest(BitcoinTestFramework): self.nodes[0].createwallet(wallet_name='wblank', disable_private_keys=False, blank=True, passphrase='thisisapassphrase') wblank = node.get_wallet_rpc('wblank') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", wblank.signmessage, "needanargument", "test") - wblank.walletpassphrase("thisisapassphrase", 999000) - assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress) - assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress) + with WalletUnlock(wblank, "thisisapassphrase"): + assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getnewaddress) + assert_raises_rpc_error(-4, "Error: This wallet has no available keys", wblank.getrawchangeaddress) self.log.info('Test creating a new encrypted wallet.') # Born encrypted wallet is created (has keys) self.nodes[0].createwallet(wallet_name='w6', disable_private_keys=False, blank=False, passphrase='thisisapassphrase') w6 = node.get_wallet_rpc('w6') assert_raises_rpc_error(-13, "Error: Please enter the wallet passphrase with walletpassphrase first.", w6.signmessage, "needanargument", "test") - w6.walletpassphrase("thisisapassphrase", 999000) - w6.signmessage(w6.getnewaddress('', 'legacy'), "test") - w6.keypoolrefill(1) - # There should only be 1 key for legacy, 3 for descriptors - walletinfo = w6.getwalletinfo() - keys = 4 if self.options.descriptors else 1 - assert_equal(walletinfo['keypoolsize'], keys) - assert_equal(walletinfo['keypoolsize_hd_internal'], keys) + with WalletUnlock(w6, "thisisapassphrase"): + w6.signmessage(w6.getnewaddress('', 'legacy'), "test") + w6.keypoolrefill(1) + # There should only be 1 key for legacy, 3 for descriptors + walletinfo = w6.getwalletinfo() + keys = 4 if self.options.descriptors else 1 + assert_equal(walletinfo['keypoolsize'], keys) + assert_equal(walletinfo['keypoolsize_hd_internal'], keys) # Allow empty passphrase, but there should be a warning resp = self.nodes[0].createwallet(wallet_name='w7', disable_private_keys=False, blank=False, passphrase='') assert_equal(resp["warnings"], [EMPTY_PASSPHRASE_MSG] if self.options.descriptors else [EMPTY_PASSPHRASE_MSG, LEGACY_WALLET_MSG]) diff --git a/test/functional/wallet_descriptor.py b/test/functional/wallet_descriptor.py index c220675eb6..e9321b72e2 100755 --- a/test/functional/wallet_descriptor.py +++ b/test/functional/wallet_descriptor.py @@ -15,6 +15,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error ) +from test_framework.wallet_util import WalletUnlock class WalletDescriptorTest(BitcoinTestFramework): @@ -128,11 +129,10 @@ class WalletDescriptorTest(BitcoinTestFramework): # Encrypt wallet 0 send_wrpc.encryptwallet('pass') - send_wrpc.walletpassphrase("pass", 999000) - addr = send_wrpc.getnewaddress() - info2 = send_wrpc.getaddressinfo(addr) - assert info1['hdmasterfingerprint'] != info2['hdmasterfingerprint'] - send_wrpc.walletlock() + with WalletUnlock(send_wrpc, "pass"): + addr = send_wrpc.getnewaddress() + info2 = send_wrpc.getaddressinfo(addr) + assert info1['hdmasterfingerprint'] != info2['hdmasterfingerprint'] assert 'hdmasterfingerprint' in send_wrpc.getaddressinfo(send_wrpc.getnewaddress()) info3 = send_wrpc.getaddressinfo(addr) assert_equal(info2['desc'], info3['desc']) @@ -142,14 +142,13 @@ class WalletDescriptorTest(BitcoinTestFramework): send_wrpc.getnewaddress() self.log.info("Test that unlock is needed when deriving only hardened keys in an encrypted wallet") - send_wrpc.walletpassphrase("pass", 999000) - send_wrpc.importdescriptors([{ - "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", - "timestamp": "now", - "range": [0,10], - "active": True - }]) - send_wrpc.walletlock() + with WalletUnlock(send_wrpc, "pass"): + send_wrpc.importdescriptors([{ + "desc": "wpkh(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/0h/*h)#y4dfsj7n", + "timestamp": "now", + "range": [0,10], + "active": True + }]) # Exhaust keypool of 100 for _ in range(100): send_wrpc.getnewaddress(address_type='bech32') diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index fdce9739eb..f50aae0c53 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -11,6 +11,7 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) +from test_framework.wallet_util import WalletUnlock def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): @@ -172,26 +173,26 @@ class WalletDumpTest(BitcoinTestFramework): # encrypt wallet, restart, unlock and dump self.nodes[0].encryptwallet('test') - self.nodes[0].walletpassphrase("test", 999000) - # Should be a no-op: - self.nodes[0].keypoolrefill() - self.nodes[0].dumpwallet(wallet_enc_dump) - - found_comments, found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \ - read_dump(wallet_enc_dump, addrs, [multisig_addr], hd_master_addr_unenc) - assert '# End of dump' in found_comments # Check that file is not corrupt - assert_equal(dump_time_str, next(c for c in found_comments if c.startswith('# * Created on'))) - assert_equal(dump_best_block_1, next(c for c in found_comments if c.startswith('# * Best block'))) - assert_equal(dump_best_block_2, next(c for c in found_comments if c.startswith('# mined on'))) - assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump - assert_equal(found_script_addr, 1) - assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now - assert_equal(found_addr_rsv, 90 * 2) - - # Overwriting should fail - assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump)) + with WalletUnlock(self.nodes[0], "test"): + # Should be a no-op: + self.nodes[0].keypoolrefill() + self.nodes[0].dumpwallet(wallet_enc_dump) + + found_comments, found_legacy_addr, found_p2sh_segwit_addr, found_bech32_addr, found_script_addr, found_addr_chg, found_addr_rsv, _ = \ + read_dump(wallet_enc_dump, addrs, [multisig_addr], hd_master_addr_unenc) + assert '# End of dump' in found_comments # Check that file is not corrupt + assert_equal(dump_time_str, next(c for c in found_comments if c.startswith('# * Created on'))) + assert_equal(dump_best_block_1, next(c for c in found_comments if c.startswith('# * Best block'))) + assert_equal(dump_best_block_2, next(c for c in found_comments if c.startswith('# mined on'))) + assert_equal(found_legacy_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_p2sh_segwit_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_bech32_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_script_addr, 1) + assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now + assert_equal(found_addr_rsv, 90 * 2) + + # Overwriting should fail + assert_raises_rpc_error(-8, "already exists", lambda: self.nodes[0].dumpwallet(wallet_enc_dump)) # Restart node with new wallet, and test importwallet self.restart_node(0) diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index e8381ba8f2..b30634010d 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -11,6 +11,7 @@ from test_framework.util import ( assert_raises_rpc_error, assert_equal, ) +from test_framework.wallet_util import WalletUnlock class WalletEncryptionTest(BitcoinTestFramework): @@ -59,19 +60,17 @@ class WalletEncryptionTest(BitcoinTestFramework): assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) # Test walletlock - self.nodes[0].walletpassphrase(passphrase, 999000) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signmessage, address, msg) # Test passphrase changes self.nodes[0].walletpassphrasechange(passphrase, passphrase2) assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) - self.nodes[0].walletpassphrase(passphrase2, 999000) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase2): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) # Test timeout bounds assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10) @@ -97,10 +96,9 @@ class WalletEncryptionTest(BitcoinTestFramework): self.nodes[0].walletpassphrasechange(passphrase2, passphrase_with_nulls) # walletpassphrasechange should not stop at null characters assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase_with_nulls.partition("\0")[0], 10) - self.nodes[0].walletpassphrase(passphrase_with_nulls, 999000) - sig = self.nodes[0].signmessage(address, msg) - assert self.nodes[0].verifymessage(address, sig, msg) - self.nodes[0].walletlock() + with WalletUnlock(self.nodes[0], passphrase_with_nulls): + sig = self.nodes[0].signmessage(address, msg) + assert self.nodes[0].verifymessage(address, sig, msg) if __name__ == '__main__': diff --git a/test/functional/wallet_fundrawtransaction.py b/test/functional/wallet_fundrawtransaction.py index 9b125d998b..77611649ac 100755 --- a/test/functional/wallet_fundrawtransaction.py +++ b/test/functional/wallet_fundrawtransaction.py @@ -25,7 +25,7 @@ from test_framework.util import ( find_vout_for_address, get_fee, ) -from test_framework.wallet_util import generate_keypair +from test_framework.wallet_util import generate_keypair, WalletUnlock ERR_NOT_ENOUGH_PRESET_INPUTS = "The preselected coins total amount does not cover the transaction target. " \ "Please allow other inputs to be automatically selected or include more coins manually" @@ -581,19 +581,18 @@ class RawTransactionsTest(BitcoinTestFramework): wallet.encryptwallet("test") if self.options.descriptors: - wallet.walletpassphrase("test", 999000) - wallet.importdescriptors([{ - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'), - 'timestamp': 'now', - 'active': True - }, - { - 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'), - 'timestamp': 'now', - 'active': True, - 'internal': True - }]) - wallet.walletlock() + with WalletUnlock(wallet, "test"): + wallet.importdescriptors([{ + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/0h/*h)'), + 'timestamp': 'now', + 'active': True + }, + { + 'desc': descsum_create('wpkh(tprv8ZgxMBicQKsPdYeeZbPSKd2KYLmeVKtcFA7kqCxDvDR13MQ6us8HopUR2wLcS2ZKPhLyKsqpDL2FtL73LMHcgoCL7DXsciA8eX8nbjCR2eG/1h/*h)'), + 'timestamp': 'now', + 'active': True, + 'internal': True + }]) # Drain the keypool. wallet.getnewaddress() @@ -619,9 +618,8 @@ class RawTransactionsTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "Transaction needs a change address, but we can't generate it.", wallet.fundrawtransaction, rawtx) # Refill the keypool. - wallet.walletpassphrase("test", 999000) - wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address - wallet.walletlock() + with WalletUnlock(wallet, "test"): + wallet.keypoolrefill(8) #need to refill the keypool to get an internal change address assert_raises_rpc_error(-13, "walletpassphrase", wallet.sendtoaddress, self.nodes[0].getnewaddress(), 1.2) @@ -634,16 +632,16 @@ class RawTransactionsTest(BitcoinTestFramework): assert fundedTx["changepos"] != -1 # Now we need to unlock. - wallet.walletpassphrase("test", 999000) - signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex']) - wallet.sendrawtransaction(signedTx['hex']) - self.generate(self.nodes[1], 1) + with WalletUnlock(wallet, "test"): + signedTx = wallet.signrawtransactionwithwallet(fundedTx['hex']) + wallet.sendrawtransaction(signedTx['hex']) + self.generate(self.nodes[1], 1) - # Make sure funds are received at node1. - assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance()) + # Make sure funds are received at node1. + assert_equal(oldBalance+Decimal('51.10000000'), self.nodes[0].getbalance()) - # Restore pre-test wallet state - wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()]) + # Restore pre-test wallet state + wallet.sendall(recipients=[df_wallet.getnewaddress(), df_wallet.getnewaddress(), df_wallet.getnewaddress()]) wallet.unloadwallet() self.generate(self.nodes[1], 1) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index 0ba8a46bae..d2341fb12e 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -9,6 +9,7 @@ from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.wallet_util import WalletUnlock class KeyPoolTest(BitcoinTestFramework): def add_options(self, parser): @@ -85,9 +86,8 @@ class KeyPoolTest(BitcoinTestFramework): assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) - nodes[0].walletpassphrase("test", 999000) - nodes[0].keypoolrefill(6) - nodes[0].walletlock() + with WalletUnlock(nodes[0], 'test'): + nodes[0].keypoolrefill(6) wi = nodes[0].getwalletinfo() if self.options.descriptors: assert_equal(wi['keypoolsize_hd_internal'], 24) @@ -131,29 +131,29 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].getnewaddress() assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getnewaddress) - nodes[0].walletpassphrase("test", 999000) - nodes[0].keypoolrefill(100) - wi = nodes[0].getwalletinfo() - if self.options.descriptors: - assert_equal(wi['keypoolsize_hd_internal'], 400) - assert_equal(wi['keypoolsize'], 400) - else: - assert_equal(wi['keypoolsize_hd_internal'], 100) - assert_equal(wi['keypoolsize'], 100) - - if not self.options.descriptors: - # Check that newkeypool entirely flushes the keypool - start_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] - start_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] - # flush keypool and get new addresses - nodes[0].newkeypool() - end_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] - end_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] - # The new keypath index should be 100 more than the old one - new_index = int(start_keypath.rsplit('/', 1)[1][:-1]) + 100 - new_change_index = int(start_change_keypath.rsplit('/', 1)[1][:-1]) + 100 - assert_equal(end_keypath, "m/0'/0'/" + str(new_index) + "'") - assert_equal(end_change_keypath, "m/0'/1'/" + str(new_change_index) + "'") + with WalletUnlock(nodes[0], 'test'): + nodes[0].keypoolrefill(100) + wi = nodes[0].getwalletinfo() + if self.options.descriptors: + assert_equal(wi['keypoolsize_hd_internal'], 400) + assert_equal(wi['keypoolsize'], 400) + else: + assert_equal(wi['keypoolsize_hd_internal'], 100) + assert_equal(wi['keypoolsize'], 100) + + if not self.options.descriptors: + # Check that newkeypool entirely flushes the keypool + start_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] + start_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] + # flush keypool and get new addresses + nodes[0].newkeypool() + end_keypath = nodes[0].getaddressinfo(nodes[0].getnewaddress())['hdkeypath'] + end_change_keypath = nodes[0].getaddressinfo(nodes[0].getrawchangeaddress())['hdkeypath'] + # The new keypath index should be 100 more than the old one + new_index = int(start_keypath.rsplit('/', 1)[1][:-1]) + 100 + new_change_index = int(start_change_keypath.rsplit('/', 1)[1][:-1]) + 100 + assert_equal(end_keypath, "m/0'/0'/" + str(new_index) + "'") + assert_equal(end_change_keypath, "m/0'/1'/" + str(new_change_index) + "'") # create a blank wallet nodes[0].createwallet(wallet_name='w2', blank=True, disable_private_keys=True) @@ -170,9 +170,9 @@ class KeyPoolTest(BitcoinTestFramework): else: res = w2.importmulti([{'desc': desc, 'timestamp': 'now'}]) assert_equal(res[0]['success'], True) - w1.walletpassphrase("test", 999000) - res = w1.sendtoaddress(address=address, amount=0.00010000) + with WalletUnlock(w1, 'test'): + res = w1.sendtoaddress(address=address, amount=0.00010000) self.generate(nodes[0], 1) destination = addr.pop() diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index 446a4551da..ec74f7705c 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -69,7 +69,8 @@ def main(): ) parser.add_argument( '--m_dir', - help='Merge inputs from this directory into the corpus_dir.', + action="append", + help="Merge inputs from these directories into the corpus_dir.", ) parser.add_argument( '-g', @@ -176,7 +177,7 @@ def main(): test_list=test_list_selection, src_dir=config['environment']['SRCDIR'], build_dir=config["environment"]["BUILDDIR"], - merge_dir=args.m_dir, + merge_dirs=[Path(m_dir) for m_dir in args.m_dir], ) return @@ -270,12 +271,13 @@ def generate_corpus(*, fuzz_pool, src_dir, build_dir, corpus_dir, targets): future.result() -def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir): - logging.info("Merge the inputs from the passed dir into the corpus_dir. Passed dir {}".format(merge_dir)) +def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dirs): + logging.info(f"Merge the inputs from the passed dir into the corpus_dir. Passed dirs {merge_dirs}") jobs = [] for t in test_list: args = [ os.path.join(build_dir, 'src', 'test', 'fuzz', 'fuzz'), + '-rss_limit_mb=8000', '-set_cover_merge=1', # set_cover_merge is used instead of -merge=1 to reduce the overall # size of the qa-assets git repository a bit, but more importantly, @@ -289,10 +291,10 @@ def merge_inputs(*, fuzz_pool, corpus, test_list, src_dir, build_dir, merge_dir) # [0] https://github.com/google/oss-fuzz/issues/1406#issuecomment-387790487 # [1] https://github.com/bitcoin-core/qa-assets/issues/130#issuecomment-1749075891 os.path.join(corpus, t), - os.path.join(merge_dir, t), - ] + ] + [str(m_dir / t) for m_dir in merge_dirs] os.makedirs(os.path.join(corpus, t), exist_ok=True) - os.makedirs(os.path.join(merge_dir, t), exist_ok=True) + for m_dir in merge_dirs: + (m_dir / t).mkdir(exist_ok=True) def job(t, args): output = 'Run {} with args {}\n'.format(t, " ".join(args)) diff --git a/test/lint/lint-shell.py b/test/lint/lint-shell.py index db84ca3d39..7fb78894af 100755 --- a/test/lint/lint-shell.py +++ b/test/lint/lint-shell.py @@ -70,7 +70,7 @@ def main(): reg = re.compile(r'src/[leveldb,secp256k1,minisketch]') def should_exclude(fname: str) -> bool: - return bool(reg.match(fname)) or 'test_utxo_snapshots.sh' in fname + return bool(reg.match(fname)) # remove everything that doesn't match this regex files[:] = [file for file in files if not should_exclude(file)] |