mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-06 13:47:56 +02:00
`shutil.rmtree` is dangerous because it recursively deletes. There are not likely to be any issues with it's current uses, but it is possible that some of the assumptions being made now won't always be true, e.g. about what some of the variables being passed to `rmtree` represent. For some remaining uses of rmtree that can't be avoided for now, use `cleanup_dir` which asserts that the recursively deleted folder is a child of the the `tmpdir` of the test run. Otherwise, `tempfile.TemporaryDirectory` should be used which does it's own deleting on being garbage collected, or old fashioned unlinking and rmdir in the case of directories with known contents.
464 lines
22 KiB
Python
Executable File
464 lines
22 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# 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 multiwallet.
|
|
|
|
Verify that a bitcoind node can load multiple wallet files
|
|
"""
|
|
from threading import Thread
|
|
import os
|
|
import platform
|
|
import shutil
|
|
import stat
|
|
|
|
from test_framework.authproxy import JSONRPCException
|
|
from test_framework.blocktools import COINBASE_MATURITY
|
|
from test_framework.test_framework import BitcoinTestFramework
|
|
from test_framework.test_node import ErrorMatch
|
|
from test_framework.util import (
|
|
assert_equal,
|
|
assert_raises_rpc_error,
|
|
ensure_for,
|
|
get_rpc_proxy,
|
|
)
|
|
|
|
got_loading_error = False
|
|
|
|
|
|
def test_load_unload(node, name):
|
|
global got_loading_error
|
|
while True:
|
|
if got_loading_error:
|
|
return
|
|
try:
|
|
node.loadwallet(name)
|
|
node.unloadwallet(name)
|
|
except JSONRPCException as e:
|
|
if e.error['code'] == -4 and 'Wallet already loading' in e.error['message']:
|
|
got_loading_error = True
|
|
return
|
|
|
|
def data_dir(node, *p):
|
|
return os.path.join(node.chain_path, *p)
|
|
|
|
def wallet_dir(node, *p):
|
|
return data_dir(node, 'wallets', *p)
|
|
|
|
def get_wallet(node, name):
|
|
return node.get_wallet_rpc(name)
|
|
|
|
|
|
class MultiWalletTest(BitcoinTestFramework):
|
|
def set_test_params(self):
|
|
self.setup_clean_chain = True
|
|
self.num_nodes = 2
|
|
self.rpc_timeout = 120
|
|
self.extra_args = [["-nowallet"], []]
|
|
|
|
def skip_test_if_missing_module(self):
|
|
self.skip_if_no_wallet()
|
|
|
|
def wallet_file(self, node, name):
|
|
if name == self.default_wallet_name:
|
|
return wallet_dir(node, self.default_wallet_name, self.wallet_data_filename)
|
|
if os.path.isdir(wallet_dir(node, name)):
|
|
return wallet_dir(node, name, "wallet.dat")
|
|
return wallet_dir(node, name)
|
|
|
|
def run_test(self):
|
|
self.check_chmod = True
|
|
self.check_symlinks = True
|
|
if platform.system() == 'Windows':
|
|
# Additional context:
|
|
# - chmod: Posix has one user per file while Windows has an ACL approach
|
|
# - symlinks: GCC 13 has FIXME notes for symlinks under Windows:
|
|
# https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/src/filesystem/ops-common.h;h=ba377905a2e90f7baf30c900b090f1f732397e08;hb=refs/heads/releases/gcc-13#l124
|
|
self.log.warning('Skipping chmod+symlink checks on Windows: '
|
|
'chmod works differently due to how access rights work and '
|
|
'symlink behavior with regard to the standard library is non-standard on cross-built binaries.')
|
|
self.check_chmod = False
|
|
self.check_symlinks = False
|
|
elif os.geteuid() == 0:
|
|
self.log.warning('Skipping checks involving chmod as they require non-root permissions.')
|
|
self.check_chmod = False
|
|
|
|
node = self.nodes[0]
|
|
|
|
assert_equal(node.listwalletdir(), {'wallets': [{'name': self.default_wallet_name, "warnings": []}]})
|
|
|
|
# check wallet.dat is created
|
|
self.stop_nodes()
|
|
assert_equal(os.path.isfile(wallet_dir(node, self.default_wallet_name, self.wallet_data_filename)), True)
|
|
|
|
self.test_scanning_main_dir_access(node)
|
|
empty_wallet, empty_created_wallet, wallet_names, in_wallet_dir = self.test_mixed_wallets(node)
|
|
self.test_scanning_sub_dir(node, in_wallet_dir)
|
|
self.test_scanning_symlink_levels(node, in_wallet_dir)
|
|
self.test_init(node, wallet_names)
|
|
self.test_balances_and_fees(node, wallet_names, in_wallet_dir)
|
|
w1, w2 = self.test_loading(node, wallet_names)
|
|
self.test_creation(node, in_wallet_dir)
|
|
self.test_unloading(node, in_wallet_dir, w1, w2)
|
|
self.test_backup_and_restore(node, wallet_names, empty_wallet, empty_created_wallet)
|
|
self.test_lock_file_closed(node)
|
|
|
|
def test_scanning_main_dir_access(self, node):
|
|
if not self.check_chmod:
|
|
return
|
|
|
|
self.log.info("Verify warning is emitted when failing to scan the wallets directory")
|
|
self.start_node(0)
|
|
with node.assert_debug_log(unexpected_msgs=['Error scanning directory entries under'], expected_msgs=[]):
|
|
result = node.listwalletdir()
|
|
assert_equal(result, {'wallets': [{'name': 'default_wallet', 'warnings': []}]})
|
|
os.chmod(data_dir(node, 'wallets'), 0)
|
|
with node.assert_debug_log(expected_msgs=['Error scanning directory entries under']):
|
|
result = node.listwalletdir()
|
|
assert_equal(result, {'wallets': []})
|
|
self.stop_node(0)
|
|
# Restore permissions
|
|
os.chmod(data_dir(node, 'wallets'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
|
|
|
def test_mixed_wallets(self, node):
|
|
self.log.info("Test mixed wallets")
|
|
# create symlink to verify wallet directory path can be referenced
|
|
# through symlink
|
|
os.mkdir(wallet_dir(node, 'w7'))
|
|
os.symlink('w7', wallet_dir(node, 'w7_symlink'))
|
|
|
|
if self.check_symlinks:
|
|
os.symlink('..', wallet_dir(node, 'recursive_dir_symlink'))
|
|
|
|
# rename wallet.dat to make sure plain wallet file paths (as opposed to
|
|
# directory paths) can be loaded
|
|
# create another dummy wallet for use in testing backups later
|
|
self.start_node(0)
|
|
node.createwallet("empty")
|
|
node.createwallet("plain")
|
|
node.createwallet("created")
|
|
self.stop_nodes()
|
|
empty_wallet = os.path.join(self.options.tmpdir, 'empty.dat')
|
|
os.rename(self.wallet_file(node, "empty"), empty_wallet)
|
|
os.rmdir(wallet_dir(node, "empty"))
|
|
empty_created_wallet = os.path.join(self.options.tmpdir, 'empty.created.dat')
|
|
os.rename(wallet_dir(node, "created", self.wallet_data_filename), empty_created_wallet)
|
|
os.rmdir(wallet_dir(node, "created"))
|
|
os.rename(self.wallet_file(node, "plain"), wallet_dir(node, "w8"))
|
|
os.rmdir(wallet_dir(node, "plain"))
|
|
|
|
# restart node with a mix of wallet names:
|
|
# w1, w2, w3 - to verify new wallets created when non-existing paths specified
|
|
# w - to verify wallet name matching works when one wallet path is prefix of another
|
|
# sub/w5 - to verify relative wallet path is created correctly
|
|
# extern/w6 - to verify absolute wallet path is created correctly
|
|
# w7_symlink - to verify symlinked wallet path is initialized correctly
|
|
# w8 - to verify existing wallet file is loaded correctly. Not tested for SQLite wallets as this is a deprecated BDB behavior.
|
|
# '' - to verify default wallet file is created correctly
|
|
to_create = ['w1', 'w2', 'w3', 'w', 'sub/w5', 'w7_symlink']
|
|
in_wallet_dir = [w.replace('/', os.path.sep) for w in to_create] # Wallets in the wallet dir
|
|
in_wallet_dir.append('w7') # w7 is not loaded or created, but will be listed by listwalletdir because w7_symlink
|
|
to_create.append(os.path.join(self.options.tmpdir, 'extern/w6')) # External, not in the wallet dir, so we need to avoid adding it to in_wallet_dir
|
|
to_load = [self.default_wallet_name]
|
|
wallet_names = to_create + to_load # Wallet names loaded in the wallet
|
|
in_wallet_dir += to_load # The loaded wallets are also in the wallet dir
|
|
self.start_node(0)
|
|
for wallet_name in to_create:
|
|
node.createwallet(wallet_name)
|
|
for wallet_name in to_load:
|
|
node.loadwallet(wallet_name)
|
|
|
|
return empty_wallet, empty_created_wallet, wallet_names, in_wallet_dir
|
|
|
|
def test_scanning_sub_dir(self, node, in_wallet_dir):
|
|
if not self.check_chmod:
|
|
return
|
|
|
|
self.log.info("Test scanning for sub directories")
|
|
# Baseline, no errors.
|
|
with node.assert_debug_log(expected_msgs=[], unexpected_msgs=["Error while scanning wallet dir"]):
|
|
walletlist = node.listwalletdir()['wallets']
|
|
assert_equal(sorted(map(lambda w: w['name'], walletlist)), sorted(in_wallet_dir))
|
|
|
|
# "Permission denied" error.
|
|
os.mkdir(wallet_dir(node, 'no_access'))
|
|
os.chmod(wallet_dir(node, 'no_access'), 0)
|
|
with node.assert_debug_log(expected_msgs=["Error while scanning wallet dir"]):
|
|
walletlist = node.listwalletdir()['wallets']
|
|
# Need to ensure access is restored for cleanup
|
|
os.chmod(wallet_dir(node, 'no_access'), stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
|
|
|
# Verify that we no longer emit errors after restoring permissions
|
|
with node.assert_debug_log(expected_msgs=[], unexpected_msgs=["Error while scanning wallet dir"]):
|
|
walletlist = node.listwalletdir()['wallets']
|
|
assert_equal(sorted(map(lambda w: w['name'], walletlist)), sorted(in_wallet_dir))
|
|
|
|
def test_scanning_symlink_levels(self, node, in_wallet_dir):
|
|
if not self.check_symlinks:
|
|
return
|
|
|
|
self.log.info("Test for errors from too many levels of symbolic links")
|
|
os.mkdir(wallet_dir(node, 'self_walletdat_symlink'))
|
|
os.symlink('wallet.dat', wallet_dir(node, 'self_walletdat_symlink/wallet.dat'))
|
|
with node.assert_debug_log(expected_msgs=["Error while scanning wallet dir"]):
|
|
walletlist = node.listwalletdir()['wallets']
|
|
assert_equal(sorted(map(lambda w: w['name'], walletlist)), sorted(in_wallet_dir))
|
|
|
|
def test_init(self, node, wallet_names):
|
|
self.log.info("Test initialization")
|
|
assert_equal(set(node.listwallets()), set(wallet_names))
|
|
# check that all requested wallets were created
|
|
self.stop_node(0)
|
|
for wallet_name in wallet_names:
|
|
assert_equal(os.path.isfile(self.wallet_file(node, wallet_name)), True)
|
|
|
|
node.assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist')
|
|
node.assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir(node))
|
|
node.assert_start_raises_init_error(['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir(node))
|
|
|
|
self.start_node(0, ['-wallet=w1', '-wallet=w1'])
|
|
self.stop_node(0, 'Warning: Ignoring duplicate -wallet w1.')
|
|
|
|
# should not initialize if wallet file is a symlink
|
|
if self.check_symlinks:
|
|
os.symlink('w8', wallet_dir(node, 'w8_symlink'))
|
|
node.assert_start_raises_init_error(['-wallet=w8_symlink'], r'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX)
|
|
|
|
# should not initialize if the specified walletdir does not exist
|
|
node.assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist')
|
|
# should not initialize if the specified walletdir is not a directory
|
|
not_a_dir = wallet_dir(node, 'notadir')
|
|
open(not_a_dir, 'a').close()
|
|
node.assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
|
|
|
|
# if wallets/ doesn't exist, datadir should be the default wallet dir
|
|
wallet_dir2 = data_dir(node, 'walletdir')
|
|
os.rename(wallet_dir(node), wallet_dir2)
|
|
self.start_node(0)
|
|
node.createwallet("w4")
|
|
node.createwallet("w5")
|
|
assert_equal(set(node.listwallets()), {"w4", "w5"})
|
|
w5 = get_wallet(node, "w5")
|
|
self.generatetoaddress(node, nblocks=1, address=w5.getnewaddress(), sync_fun=self.no_op)
|
|
|
|
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
|
|
os.rename(wallet_dir2, wallet_dir(node))
|
|
self.restart_node(0, ['-nowallet', '-walletdir=' + data_dir(node)])
|
|
node.loadwallet("w4")
|
|
node.loadwallet("w5")
|
|
assert_equal(set(node.listwallets()), {"w4", "w5"})
|
|
w5 = get_wallet(node, "w5")
|
|
assert_equal(w5.getbalances()["mine"]["immature"], 50)
|
|
|
|
competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir')
|
|
os.mkdir(competing_wallet_dir)
|
|
self.restart_node(0, ['-nowallet', '-walletdir=' + competing_wallet_dir])
|
|
node.createwallet(self.default_wallet_name)
|
|
exp_stderr = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['CLIENT_NAME']}?"
|
|
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
|
|
|
|
def test_balances_and_fees(self, node, wallet_names, in_wallet_dir):
|
|
self.log.info("Test balances and fees")
|
|
self.restart_node(0)
|
|
for wallet_name in wallet_names:
|
|
node.loadwallet(wallet_name)
|
|
|
|
assert_equal(sorted(map(lambda w: w['name'], node.listwalletdir()['wallets'])), sorted(in_wallet_dir))
|
|
|
|
wallets = [get_wallet(node, w) for w in wallet_names]
|
|
wallet_bad = get_wallet(node, "bad")
|
|
|
|
# check wallet names and balances
|
|
self.generatetoaddress(node, nblocks=1, address=wallets[0].getnewaddress(), sync_fun=self.no_op)
|
|
for wallet_name, wallet in zip(wallet_names, wallets):
|
|
info = wallet.getwalletinfo()
|
|
assert_equal(wallet.getbalances()["mine"]["immature"], 50 if wallet is wallets[0] else 0)
|
|
assert_equal(info['walletname'], wallet_name)
|
|
|
|
# accessing invalid wallet fails
|
|
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo)
|
|
|
|
# accessing wallet RPC without using wallet endpoint fails
|
|
assert_raises_rpc_error(-19, "Multiple wallets are loaded. Please select which wallet", node.getwalletinfo)
|
|
|
|
w1, w2, w3, w4, *_ = wallets
|
|
self.generatetoaddress(node, nblocks=COINBASE_MATURITY + 1, address=w1.getnewaddress(), sync_fun=self.no_op)
|
|
assert_equal(w1.getbalance(), 100)
|
|
assert_equal(w2.getbalance(), 0)
|
|
assert_equal(w3.getbalance(), 0)
|
|
assert_equal(w4.getbalance(), 0)
|
|
|
|
w1.sendtoaddress(w2.getnewaddress(), 1)
|
|
w1.sendtoaddress(w3.getnewaddress(), 2)
|
|
w1.sendtoaddress(w4.getnewaddress(), 3)
|
|
self.generatetoaddress(node, nblocks=1, address=w1.getnewaddress(), sync_fun=self.no_op)
|
|
assert_equal(w2.getbalance(), 1)
|
|
assert_equal(w3.getbalance(), 2)
|
|
assert_equal(w4.getbalance(), 3)
|
|
|
|
batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()])
|
|
assert_equal(batch[0]["result"]["chain"], self.chain)
|
|
assert_equal(batch[1]["result"]["walletname"], "w1")
|
|
|
|
def test_loading(self, node, wallet_names):
|
|
self.log.info("Test dynamic wallet loading")
|
|
|
|
self.restart_node(0, ['-nowallet'])
|
|
assert_equal(node.listwallets(), [])
|
|
assert_raises_rpc_error(-18, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)", node.getwalletinfo)
|
|
|
|
self.log.info("Load first wallet")
|
|
loadwallet_name = node.loadwallet(wallet_names[0])
|
|
assert_equal(loadwallet_name['name'], wallet_names[0])
|
|
assert_equal(node.listwallets(), wallet_names[0:1])
|
|
node.getwalletinfo()
|
|
w1 = get_wallet(node, wallet_names[0])
|
|
w1.getwalletinfo()
|
|
|
|
self.log.info("Load second wallet")
|
|
loadwallet_name = node.loadwallet(wallet_names[1])
|
|
assert_equal(loadwallet_name['name'], wallet_names[1])
|
|
assert_equal(node.listwallets(), wallet_names[0:2])
|
|
assert_raises_rpc_error(-19, "Multiple wallets are loaded. Please select which wallet", node.getwalletinfo)
|
|
w2 = get_wallet(node, wallet_names[1])
|
|
w2.getwalletinfo()
|
|
|
|
self.log.info("Concurrent wallet loading")
|
|
threads = []
|
|
for _ in range(3):
|
|
n = node.cli if self.options.usecli else get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir)
|
|
t = Thread(target=test_load_unload, args=(n, wallet_names[2]))
|
|
t.start()
|
|
threads.append(t)
|
|
for t in threads:
|
|
t.join()
|
|
global got_loading_error
|
|
assert_equal(got_loading_error, True)
|
|
|
|
self.log.info("Load remaining wallets")
|
|
for wallet_name in wallet_names[2:]:
|
|
loadwallet_name = node.loadwallet(wallet_name)
|
|
assert_equal(loadwallet_name['name'], wallet_name)
|
|
|
|
assert_equal(set(node.listwallets()), set(wallet_names))
|
|
|
|
# Fail to load if wallet doesn't exist
|
|
path = wallet_dir(node, "wallets")
|
|
assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Path does not exist.".format(path), node.loadwallet, 'wallets')
|
|
|
|
# Fail to load duplicate wallets
|
|
assert_raises_rpc_error(-35, "Wallet \"w1\" is already loaded.", node.loadwallet, wallet_names[0])
|
|
# Fail to load if wallet file is a symlink
|
|
if self.check_symlinks:
|
|
assert_raises_rpc_error(-4, "Wallet file verification failed. Invalid -wallet path 'w8_symlink'", node.loadwallet, 'w8_symlink')
|
|
|
|
# Fail to load if a directory is specified that doesn't contain a wallet
|
|
os.mkdir(wallet_dir(node, 'empty_wallet_dir'))
|
|
path = wallet_dir(node, "empty_wallet_dir")
|
|
assert_raises_rpc_error(-18, "Wallet file verification failed. Failed to load database path '{}'. Data is not in recognized format.".format(path), node.loadwallet, 'empty_wallet_dir')
|
|
|
|
return w1, w2
|
|
|
|
def test_creation(self, node, in_wallet_dir):
|
|
self.log.info("Test dynamic wallet creation")
|
|
|
|
# should raise rpc error if wallet path can't be created
|
|
err_code = -4
|
|
assert_raises_rpc_error(err_code, "Wallet file verification failed. ", node.createwallet, "w8/bad")
|
|
|
|
# Fail to create a wallet if it already exists.
|
|
path = wallet_dir(node, "w2")
|
|
assert_raises_rpc_error(-4, "Failed to create database path '{}'. Database already exists.".format(path), node.createwallet, 'w2')
|
|
|
|
# Successfully create a wallet with a new name
|
|
loadwallet_name = node.createwallet('w9')
|
|
in_wallet_dir.append('w9')
|
|
assert_equal(loadwallet_name['name'], 'w9')
|
|
w9 = get_wallet(node, 'w9')
|
|
assert_equal(w9.getwalletinfo()['walletname'], 'w9')
|
|
|
|
assert 'w9' in node.listwallets()
|
|
|
|
# Successfully create a wallet using a full path
|
|
new_wallet_dir = os.path.join(self.options.tmpdir, 'new_walletdir')
|
|
new_wallet_name = os.path.join(new_wallet_dir, 'w10')
|
|
loadwallet_name = node.createwallet(new_wallet_name)
|
|
assert_equal(loadwallet_name['name'], new_wallet_name)
|
|
w10 = get_wallet(node, new_wallet_name)
|
|
assert_equal(w10.getwalletinfo()['walletname'], new_wallet_name)
|
|
|
|
assert new_wallet_name in node.listwallets()
|
|
|
|
def test_unloading(self, node, in_wallet_dir, w1, w2):
|
|
self.log.info("Test dynamic wallet unloading")
|
|
|
|
# Test `unloadwallet` errors
|
|
assert_raises_rpc_error(-8, "Either the RPC endpoint wallet or the wallet name parameter must be provided", node.unloadwallet)
|
|
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", node.unloadwallet, "dummy")
|
|
assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", get_wallet(node, "dummy").unloadwallet)
|
|
assert_raises_rpc_error(-8, "The RPC endpoint wallet and the wallet name parameter specify different wallets", w1.unloadwallet, "w2"),
|
|
|
|
# Successfully unload the specified wallet name
|
|
node.unloadwallet("w1")
|
|
assert 'w1' not in node.listwallets()
|
|
|
|
# Unload w1 again, this time providing the wallet name twice
|
|
node.loadwallet("w1")
|
|
assert 'w1' in node.listwallets()
|
|
w1.unloadwallet("w1")
|
|
assert 'w1' not in node.listwallets()
|
|
|
|
# Successfully unload the wallet referenced by the request endpoint
|
|
# Also ensure unload works during walletpassphrase timeout
|
|
w2.encryptwallet('test')
|
|
w2.walletpassphrase('test', 1)
|
|
w2.unloadwallet()
|
|
ensure_for(duration=1.1, f=lambda: 'w2' not in node.listwallets())
|
|
|
|
# Successfully unload all wallets
|
|
for wallet_name in node.listwallets():
|
|
node.unloadwallet(wallet_name)
|
|
assert_equal(node.listwallets(), [])
|
|
assert_raises_rpc_error(-18, "No wallet is loaded. Load a wallet using loadwallet or create a new one with createwallet. (Note: A default wallet is no longer automatically created)", node.getwalletinfo)
|
|
|
|
# Successfully load a previously unloaded wallet
|
|
node.loadwallet('w1')
|
|
assert_equal(node.listwallets(), ['w1'])
|
|
assert_equal(w1.getwalletinfo()['walletname'], 'w1')
|
|
|
|
assert_equal(sorted(map(lambda w: w['name'], node.listwalletdir()['wallets'])), sorted(in_wallet_dir))
|
|
|
|
def test_backup_and_restore(self, node, wallet_names, empty_wallet, empty_created_wallet):
|
|
self.log.info("Test wallet backup and restore")
|
|
self.restart_node(0, ['-nowallet'])
|
|
for wallet_name in wallet_names:
|
|
node.loadwallet(wallet_name)
|
|
for wallet_name in wallet_names:
|
|
rpc = get_wallet(node, wallet_name)
|
|
addr = rpc.getnewaddress()
|
|
backup = os.path.join(self.options.tmpdir, 'backup.dat')
|
|
if os.path.exists(backup):
|
|
os.unlink(backup)
|
|
rpc.backupwallet(backup)
|
|
node.unloadwallet(wallet_name)
|
|
shutil.copyfile(empty_created_wallet if wallet_name == self.default_wallet_name else empty_wallet, self.wallet_file(node, wallet_name))
|
|
node.loadwallet(wallet_name)
|
|
assert_equal(rpc.getaddressinfo(addr)['ismine'], False)
|
|
node.unloadwallet(wallet_name)
|
|
shutil.copyfile(backup, self.wallet_file(node, wallet_name))
|
|
node.loadwallet(wallet_name)
|
|
assert_equal(rpc.getaddressinfo(addr)['ismine'], True)
|
|
|
|
def test_lock_file_closed(self, node):
|
|
self.log.info("Test wallet lock file is closed")
|
|
self.start_node(1)
|
|
wallet = os.path.join(self.options.tmpdir, 'my_wallet')
|
|
node.createwallet(wallet)
|
|
assert_raises_rpc_error(-4, "Unable to obtain an exclusive lock", self.nodes[1].loadwallet, wallet)
|
|
node.unloadwallet(wallet)
|
|
self.nodes[1].loadwallet(wallet)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
MultiWalletTest(__file__).main()
|