2017-07-13 11:49:46 -04:00
#!/usr/bin/env python3
2022-12-24 20:49:50 -03:00
# Copyright (c) 2017-2022 The Bitcoin Core developers
2017-07-13 11:49:46 -04:00
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-06-15 09:05:32 -04:00
""" Test multiwallet.
Verify that a bitcoind node can load multiple wallet files
"""
2020-03-30 16:59:04 -03:00
from decimal import Decimal
2020-06-17 09:00:25 -04:00
from threading import Thread
2017-07-26 19:57:02 -04:00
import os
2023-12-08 13:30:19 -03:00
import platform
2017-10-10 16:27:26 -03:00
import shutil
2020-07-12 23:02:26 -04:00
import stat
2018-10-19 19:10:49 -03:00
import time
2017-07-26 19:57:02 -04:00
2020-06-17 09:00:25 -04:00
from test_framework . authproxy import JSONRPCException
2021-05-17 10:38:19 -04:00
from test_framework . blocktools import COINBASE_MATURITY
2017-07-13 11:49:46 -04:00
from test_framework . test_framework import BitcoinTestFramework
2018-03-28 10:37:09 -03:00
from test_framework . test_node import ErrorMatch
2018-01-02 10:57:27 -03:00
from test_framework . util import (
assert_equal ,
assert_raises_rpc_error ,
2020-06-17 09:00:25 -04:00
get_rpc_proxy ,
2018-01-02 10:57:27 -03:00
)
2020-06-17 09:00:25 -04:00
got_loading_error = False
2020-12-04 06:46:30 -03:00
2020-06-17 09:00:25 -04:00
def test_load_unload ( node , name ) :
global got_loading_error
2020-12-04 06:31:24 -03:00
while True :
2020-06-17 09:00:25 -04:00
if got_loading_error :
return
try :
node . loadwallet ( name )
node . unloadwallet ( name )
except JSONRPCException as e :
2021-02-03 19:19:11 -03:00
if e . error [ ' code ' ] == - 4 and ' Wallet already loading ' in e . error [ ' message ' ] :
2020-06-17 09:00:25 -04:00
got_loading_error = True
return
2017-07-13 11:49:46 -04:00
class MultiWalletTest ( BitcoinTestFramework ) :
2017-06-09 18:21:21 -04:00
def set_test_params ( self ) :
2017-07-13 11:49:46 -04:00
self . setup_clean_chain = True
2017-12-14 19:15:18 -03:00
self . num_nodes = 2
2020-03-13 12:10:36 -03:00
self . rpc_timeout = 120
2020-10-19 09:24:27 -03:00
self . extra_args = [ [ " -nowallet " ] , [ ] ]
2017-07-13 11:49:46 -04:00
2018-09-09 14:32:37 -03:00
def skip_test_if_missing_module ( self ) :
self . skip_if_no_wallet ( )
2019-08-19 17:13:11 -04:00
def add_options ( self , parser ) :
2022-11-09 08:53:13 -03:00
self . add_wallet_options ( parser )
2019-08-19 17:13:11 -04:00
parser . add_argument (
' --data_wallets_dir ' ,
default = os . path . join ( os . path . dirname ( os . path . realpath ( __file__ ) ) , ' data/wallets/ ' ) ,
help = ' Test data with wallet directories (default: %(default)s ) ' ,
)
2017-07-13 11:49:46 -04:00
def run_test ( self ) :
2017-12-20 20:37:34 -03:00
node = self . nodes [ 0 ]
scripted-diff: Use wallets_path and chain_path where possible
Instead of passing the datadir and chain name to os.path.join, just use
the existing properties, which are the same.
-BEGIN VERIFY SCRIPT-
sed -i --regexp-extended 's|\.datadir, self\.chain, .wallets.|.wallets_path|g' $(git grep -l '\.datadir, self\.chain,')
sed -i --regexp-extended 's|\.datadir, self\.chain,|.chain_path,|g' $(git grep -l '\.datadir, self\.chain,')
-END VERIFY SCRIPT-
2023-06-20 05:22:03 -04:00
data_dir = lambda * p : os . path . join ( node . chain_path , * p )
2017-12-20 20:37:34 -03:00
wallet_dir = lambda * p : data_dir ( ' wallets ' , * p )
wallet = lambda name : node . get_wallet_rpc ( name )
2018-07-15 05:17:50 -04:00
def wallet_file ( name ) :
2020-10-07 15:50:05 -03:00
if name == self . default_wallet_name :
return wallet_dir ( self . default_wallet_name , self . wallet_data_filename )
2018-07-15 05:17:50 -04:00
if os . path . isdir ( wallet_dir ( name ) ) :
2020-10-07 15:50:05 -03:00
return wallet_dir ( name , " wallet.dat " )
2018-07-15 05:17:50 -04:00
return wallet_dir ( name )
2020-12-04 06:46:30 -03:00
assert_equal ( self . nodes [ 0 ] . listwalletdir ( ) , { ' wallets ' : [ { ' name ' : self . default_wallet_name } ] } )
2018-09-24 19:56:32 -03:00
2017-11-14 15:32:41 -03:00
# check wallet.dat is created
2017-12-14 19:15:18 -03:00
self . stop_nodes ( )
2020-09-28 21:24:06 -03:00
assert_equal ( os . path . isfile ( wallet_dir ( self . default_wallet_name , self . wallet_data_filename ) ) , True )
2017-11-14 15:32:41 -03:00
2017-11-15 17:44:36 -03:00
# create symlink to verify wallet directory path can be referenced
# through symlink
2018-10-24 01:38:04 -03:00
os . mkdir ( wallet_dir ( ' w7 ' ) )
os . symlink ( ' w7 ' , wallet_dir ( ' w7_symlink ' ) )
2017-11-15 17:44:36 -03:00
2020-07-12 23:02:26 -04:00
os . symlink ( ' .. ' , wallet_dir ( ' recursive_dir_symlink ' ) )
os . mkdir ( wallet_dir ( ' self_walletdat_symlink ' ) )
os . symlink ( ' wallet.dat ' , wallet_dir ( ' self_walletdat_symlink/wallet.dat ' ) )
2017-11-15 17:44:36 -03:00
# rename wallet.dat to make sure plain wallet file paths (as opposed to
# directory paths) can be loaded
2018-07-15 05:17:50 -04:00
# create another dummy wallet for use in testing backups later
2020-10-19 09:24:27 -03:00
self . start_node ( 0 )
2020-10-29 15:51:32 -03:00
node . createwallet ( " empty " )
node . createwallet ( " plain " )
2020-10-07 15:50:05 -03:00
node . createwallet ( " created " )
2018-07-15 05:17:50 -04:00
self . stop_nodes ( )
empty_wallet = os . path . join ( self . options . tmpdir , ' empty.dat ' )
2020-10-07 15:50:05 -03:00
os . rename ( wallet_file ( " empty " ) , empty_wallet )
shutil . rmtree ( wallet_dir ( " empty " ) )
empty_created_wallet = os . path . join ( self . options . tmpdir , ' empty.created.dat ' )
os . rename ( wallet_dir ( " created " , self . wallet_data_filename ) , empty_created_wallet )
shutil . rmtree ( wallet_dir ( " created " ) )
os . rename ( wallet_file ( " plain " ) , wallet_dir ( " w8 " ) )
shutil . rmtree ( wallet_dir ( " plain " ) )
2018-07-15 05:17:50 -04:00
2017-11-14 15:32:41 -03:00
# 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
2017-11-15 17:44:36 -03:00
# w7_symlink - to verify symlinked wallet path is initialized correctly
2020-10-29 15:51:32 -03:00
# w8 - to verify existing wallet file is loaded correctly. Not tested for SQLite wallets as this is a deprecated BDB behavior.
2017-11-15 17:44:36 -03:00
# '' - to verify default wallet file is created correctly
2020-10-29 15:51:32 -03:00
to_create = [ ' w1 ' , ' w2 ' , ' w3 ' , ' w ' , ' sub/w5 ' , ' w7_symlink ' ]
2020-11-05 09:52:34 -03:00
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
2020-10-29 15:51:32 -03:00
to_load = [ self . default_wallet_name ]
if not self . options . descriptors :
to_load . append ( ' w8 ' )
2020-11-05 09:52:34 -03:00
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
2020-10-19 09:24:27 -03:00
self . start_node ( 0 )
2020-10-29 15:51:32 -03:00
for wallet_name in to_create :
self . nodes [ 0 ] . createwallet ( wallet_name )
for wallet_name in to_load :
2020-10-19 09:24:27 -03:00
self . nodes [ 0 ] . loadwallet ( wallet_name )
2020-07-12 23:02:26 -04:00
os . mkdir ( wallet_dir ( ' no_access ' ) )
os . chmod ( wallet_dir ( ' no_access ' ) , 0 )
try :
2020-12-01 09:48:52 -03:00
with self . nodes [ 0 ] . assert_debug_log ( expected_msgs = [ ' Error scanning ' ] ) :
2020-07-12 23:02:26 -04:00
walletlist = self . nodes [ 0 ] . listwalletdir ( ) [ ' wallets ' ]
finally :
# Need to ensure access is restored for cleanup
os . chmod ( wallet_dir ( ' no_access ' ) , stat . S_IRUSR | stat . S_IWUSR | stat . S_IXUSR )
assert_equal ( sorted ( map ( lambda w : w [ ' name ' ] , walletlist ) ) , sorted ( in_wallet_dir ) )
2018-09-24 19:56:32 -03:00
2017-11-14 15:32:41 -03:00
assert_equal ( set ( node . listwallets ( ) ) , set ( wallet_names ) )
2020-10-19 09:24:27 -03:00
# should raise rpc error if wallet path can't be created
2020-10-29 15:51:32 -03:00
err_code = - 4 if self . options . descriptors else - 1
2023-12-08 13:30:19 -03:00
assert_raises_rpc_error ( err_code , " filesystem error: " if platform . system ( ) != ' Windows ' else " create_directories: " , self . nodes [ 0 ] . createwallet , " w8/bad " )
2020-10-19 09:24:27 -03:00
2017-11-14 15:32:41 -03:00
# check that all requested wallets were created
self . stop_node ( 0 )
for wallet_name in wallet_names :
2018-07-15 05:17:50 -04:00
assert_equal ( os . path . isfile ( wallet_file ( wallet_name ) ) , True )
2017-11-14 15:32:41 -03:00
2018-02-07 11:36:13 -03:00
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -walletdir=wallets ' ] , ' Error: Specified -walletdir " wallets " does not exist ' )
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -walletdir=wallets ' ] , ' Error: Specified -walletdir " wallets " is a relative path ' , cwd = data_dir ( ) )
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -walletdir=debug.log ' ] , ' Error: Specified -walletdir " debug.log " is not a directory ' , cwd = data_dir ( ) )
2018-01-18 15:15:00 -03:00
2020-10-20 12:48:20 -03:00
self . start_node ( 0 , [ ' -wallet=w1 ' , ' -wallet=w1 ' ] )
self . stop_node ( 0 , ' Warning: Ignoring duplicate -wallet w1. ' )
2017-07-26 19:56:30 -04:00
2020-10-29 15:51:32 -03:00
if not self . options . descriptors :
# Only BDB doesn't open duplicate wallet files. SQLite does not have this limitation. While this may be desired in the future, it is not necessary
# should not initialize if one wallet is a copy of another
shutil . copyfile ( wallet_dir ( ' w8 ' ) , wallet_dir ( ' w8_copy ' ) )
in_wallet_dir . append ( ' w8_copy ' )
exp_stderr = r " BerkeleyDatabase: Can ' t open database w8_copy \ (duplicates fileid \ w+ from w8 \ ) "
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -wallet=w8 ' , ' -wallet=w8_copy ' ] , exp_stderr , match = ErrorMatch . PARTIAL_REGEX )
2017-10-10 16:27:26 -03:00
2017-07-26 19:57:02 -04:00
# should not initialize if wallet file is a symlink
2018-10-24 01:38:04 -03:00
os . symlink ( ' w8 ' , wallet_dir ( ' w8_symlink ' ) )
2019-02-01 22:00:02 -03:00
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -wallet=w8_symlink ' ] , r ' Error: Invalid -wallet path \' w8_symlink \' \ . .* ' , match = ErrorMatch . FULL_REGEX )
2017-07-26 19:57:02 -04:00
2017-10-09 05:32:07 -03:00
# should not initialize if the specified walletdir does not exist
2018-02-07 11:36:13 -03:00
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -walletdir=bad ' ] , ' Error: Specified -walletdir " bad " does not exist ' )
2017-11-17 21:36:37 -03:00
# should not initialize if the specified walletdir is not a directory
2017-12-20 20:37:34 -03:00
not_a_dir = wallet_dir ( ' notadir ' )
2018-06-12 11:49:20 -04:00
open ( not_a_dir , ' a ' , encoding = " utf8 " ) . close ( )
2018-03-28 10:37:09 -03:00
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -walletdir= ' + not_a_dir ] , ' Error: Specified -walletdir " ' + not_a_dir + ' " is not a directory ' )
2017-10-09 05:32:07 -03:00
2020-04-21 18:55:20 -04:00
self . log . info ( " Do not allow -upgradewallet with multiwallet " )
self . nodes [ 0 ] . assert_start_raises_init_error ( [ ' -upgradewallet ' ] , " Error: Error parsing command line arguments: Invalid parameter -upgradewallet " )
2017-10-12 06:04:46 -03:00
# if wallets/ doesn't exist, datadir should be the default wallet dir
2017-12-20 20:37:34 -03:00
wallet_dir2 = data_dir ( ' walletdir ' )
os . rename ( wallet_dir ( ) , wallet_dir2 )
2020-10-19 09:24:27 -03:00
self . start_node ( 0 )
self . nodes [ 0 ] . createwallet ( " w4 " )
self . nodes [ 0 ] . createwallet ( " w5 " )
2017-12-20 20:37:34 -03:00
assert_equal ( set ( node . listwallets ( ) ) , { " w4 " , " w5 " } )
w5 = wallet ( " w5 " )
2020-11-10 14:02:31 -03:00
self . generatetoaddress ( node , nblocks = 1 , address = w5 . getnewaddress ( ) , sync_fun = self . no_op )
2017-10-12 06:04:46 -03:00
# now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded
2017-12-20 20:37:34 -03:00
os . rename ( wallet_dir2 , wallet_dir ( ) )
2020-10-19 09:24:27 -03:00
self . restart_node ( 0 , [ ' -nowallet ' , ' -walletdir= ' + data_dir ( ) ] )
self . nodes [ 0 ] . loadwallet ( " w4 " )
self . nodes [ 0 ] . loadwallet ( " w5 " )
2017-12-20 20:37:34 -03:00
assert_equal ( set ( node . listwallets ( ) ) , { " w4 " , " w5 " } )
w5 = wallet ( " w5 " )
2017-10-09 05:32:07 -03:00
w5_info = w5 . getwalletinfo ( )
2017-10-12 06:04:46 -03:00
assert_equal ( w5_info [ ' immature_balance ' ] , 50 )
2017-10-09 05:32:07 -03:00
2017-12-14 19:15:18 -03:00
competing_wallet_dir = os . path . join ( self . options . tmpdir , ' competing_walletdir ' )
os . mkdir ( competing_wallet_dir )
2020-10-19 09:24:27 -03:00
self . restart_node ( 0 , [ ' -nowallet ' , ' -walletdir= ' + competing_wallet_dir ] )
2020-10-29 15:51:32 -03:00
self . nodes [ 0 ] . createwallet ( self . default_wallet_name )
if self . options . descriptors :
2021-10-19 17:53:23 -03:00
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 ' ] [ ' PACKAGE_NAME ' ] } ? "
2020-10-29 15:51:32 -03:00
else :
exp_stderr = r " Error: Error initializing wallet database environment \" \ S+competing_walletdir \ S* \" ! "
2018-03-28 10:37:09 -03:00
self . nodes [ 1 ] . assert_start_raises_init_error ( [ ' -walletdir= ' + competing_wallet_dir ] , exp_stderr , match = ErrorMatch . PARTIAL_REGEX )
2017-10-09 05:32:07 -03:00
2020-10-19 09:24:27 -03:00
self . restart_node ( 0 )
for wallet_name in wallet_names :
self . nodes [ 0 ] . loadwallet ( wallet_name )
2017-07-26 19:56:30 -04:00
2020-10-29 15:51:32 -03:00
assert_equal ( sorted ( map ( lambda w : w [ ' name ' ] , self . nodes [ 0 ] . listwalletdir ( ) [ ' wallets ' ] ) ) , sorted ( in_wallet_dir ) )
2018-09-24 19:56:32 -03:00
2017-11-14 15:32:41 -03:00
wallets = [ wallet ( w ) for w in wallet_names ]
2017-12-20 20:37:34 -03:00
wallet_bad = wallet ( " bad " )
2017-06-02 14:30:36 -04:00
2017-11-14 15:32:41 -03:00
# check wallet names and balances
2020-11-10 14:02:31 -03:00
self . generatetoaddress ( node , nblocks = 1 , address = wallets [ 0 ] . getnewaddress ( ) , sync_fun = self . no_op )
2017-11-14 15:32:41 -03:00
for wallet_name , wallet in zip ( wallet_names , wallets ) :
info = wallet . getwalletinfo ( )
assert_equal ( info [ ' immature_balance ' ] , 50 if wallet is wallets [ 0 ] else 0 )
assert_equal ( info [ ' walletname ' ] , wallet_name )
2017-07-13 11:49:46 -04:00
2017-07-17 05:42:30 -04:00
# accessing invalid wallet fails
2017-07-12 10:33:46 -04:00
assert_raises_rpc_error ( - 18 , " Requested wallet does not exist or is not loaded " , wallet_bad . getwalletinfo )
2017-07-17 05:42:30 -04:00
2017-06-15 09:05:32 -04:00
# accessing wallet RPC without using wallet endpoint fails
2017-12-20 20:37:34 -03:00
assert_raises_rpc_error ( - 19 , " Wallet file not specified " , node . getwalletinfo )
2017-07-13 11:49:46 -04:00
2017-11-14 15:32:41 -03:00
w1 , w2 , w3 , w4 , * _ = wallets
2020-11-10 14:02:31 -03:00
self . generatetoaddress ( node , nblocks = COINBASE_MATURITY + 1 , address = w1 . getnewaddress ( ) , sync_fun = self . no_op )
2017-07-13 11:49:46 -04:00
assert_equal ( w1 . getbalance ( ) , 100 )
assert_equal ( w2 . getbalance ( ) , 0 )
assert_equal ( w3 . getbalance ( ) , 0 )
2017-11-15 13:03:49 -03:00
assert_equal ( w4 . getbalance ( ) , 0 )
2017-07-13 11:49:46 -04:00
w1 . sendtoaddress ( w2 . getnewaddress ( ) , 1 )
w1 . sendtoaddress ( w3 . getnewaddress ( ) , 2 )
2017-11-15 13:03:49 -03:00
w1 . sendtoaddress ( w4 . getnewaddress ( ) , 3 )
2020-11-10 14:02:31 -03:00
self . generatetoaddress ( node , nblocks = 1 , address = w1 . getnewaddress ( ) , sync_fun = self . no_op )
2017-07-13 11:49:46 -04:00
assert_equal ( w2 . getbalance ( ) , 1 )
assert_equal ( w3 . getbalance ( ) , 2 )
2017-11-15 13:03:49 -03:00
assert_equal ( w4 . getbalance ( ) , 3 )
2017-07-13 11:49:46 -04:00
2017-09-07 18:40:25 -03:00
batch = w1 . batch ( [ w1 . getblockchaininfo . get_request ( ) , w1 . getwalletinfo . get_request ( ) ] )
2019-08-22 12:17:31 -04:00
assert_equal ( batch [ 0 ] [ " result " ] [ " chain " ] , self . chain )
2017-09-07 18:40:25 -03:00
assert_equal ( batch [ 1 ] [ " result " ] [ " walletname " ] , " w1 " )
2018-04-07 13:12:46 -03:00
self . log . info ( ' Check for per-wallet settxfee call ' )
assert_equal ( w1 . getwalletinfo ( ) [ ' paytxfee ' ] , 0 )
assert_equal ( w2 . getwalletinfo ( ) [ ' paytxfee ' ] , 0 )
2020-03-30 16:59:04 -03:00
w2 . settxfee ( 0.001 )
2018-04-07 13:12:46 -03:00
assert_equal ( w1 . getwalletinfo ( ) [ ' paytxfee ' ] , 0 )
2020-03-30 16:59:04 -03:00
assert_equal ( w2 . getwalletinfo ( ) [ ' paytxfee ' ] , Decimal ( ' 0.00100000 ' ) )
2018-04-07 13:12:46 -03:00
2018-04-18 17:32:16 -03:00
self . log . info ( " Test dynamic wallet loading " )
self . restart_node ( 0 , [ ' -nowallet ' ] )
assert_equal ( node . listwallets ( ) , [ ] )
2020-10-07 22:36:44 -03:00
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 )
2018-04-18 17:32:16 -03:00
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 = node . get_wallet_rpc ( 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 , " Wallet file not specified " , node . getwalletinfo )
w2 = node . get_wallet_rpc ( wallet_names [ 1 ] )
w2 . getwalletinfo ( )
2020-06-17 09:00:25 -04:00
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 )
2020-12-04 06:46:30 -03:00
t = Thread ( target = test_load_unload , args = ( n , wallet_names [ 2 ] ) )
2020-06-17 09:00:25 -04:00
t . start ( )
threads . append ( t )
for t in threads :
t . join ( )
global got_loading_error
assert_equal ( got_loading_error , True )
2018-04-18 17:32:16 -03:00
self . log . info ( " Load remaining wallets " )
for wallet_name in wallet_names [ 2 : ] :
loadwallet_name = self . nodes [ 0 ] . loadwallet ( wallet_name )
assert_equal ( loadwallet_name [ ' name ' ] , wallet_name )
assert_equal ( set ( self . nodes [ 0 ] . listwallets ( ) ) , set ( wallet_names ) )
# Fail to load if wallet doesn't exist
2023-06-14 06:28:54 -04:00
path = wallet_dir ( " wallets " )
2020-08-04 20:58:43 -04:00
assert_raises_rpc_error ( - 18 , " Wallet file verification failed. Failed to load database path ' {} ' . Path does not exist. " . format ( path ) , self . nodes [ 0 ] . loadwallet , ' wallets ' )
2018-04-18 17:32:16 -03:00
# Fail to load duplicate wallets
2022-10-01 18:52:17 -03:00
assert_raises_rpc_error ( - 35 , " Wallet \" w1 \" is already loaded. " , self . nodes [ 0 ] . loadwallet , wallet_names [ 0 ] )
if not self . options . descriptors :
2020-10-29 15:51:32 -03:00
# This tests the default wallet that BDB makes, so SQLite wallet doesn't need to test this
# Fail to load duplicate wallets by different ways (directory and filepath)
2023-06-14 06:28:54 -04:00
path = wallet_dir ( " wallet.dat " )
2021-01-19 11:15:02 -03:00
assert_raises_rpc_error ( - 35 , " Wallet file verification failed. Refusing to load database. Data file ' {} ' is already loaded. " . format ( path ) , self . nodes [ 0 ] . loadwallet , ' wallet.dat ' )
2018-10-23 02:26:27 -03:00
2020-10-29 15:51:32 -03:00
# Only BDB doesn't open duplicate wallet files. SQLite does not have this limitation. While this may be desired in the future, it is not necessary
# Fail to load if one wallet is a copy of another
assert_raises_rpc_error ( - 4 , " BerkeleyDatabase: Can ' t open database w8_copy (duplicates fileid " , self . nodes [ 0 ] . loadwallet , ' w8_copy ' )
2018-04-18 17:32:16 -03:00
2020-10-29 15:51:32 -03:00
# Fail to load if one wallet is a copy of another, test this twice to make sure that we don't re-introduce #14304
assert_raises_rpc_error ( - 4 , " BerkeleyDatabase: Can ' t open database w8_copy (duplicates fileid " , self . nodes [ 0 ] . loadwallet , ' w8_copy ' )
2018-10-24 01:33:50 -03:00
2018-04-18 17:32:16 -03:00
# Fail to load if wallet file is a symlink
2020-04-21 07:53:24 -04:00
assert_raises_rpc_error ( - 4 , " Wallet file verification failed. Invalid -wallet path ' w8_symlink ' " , self . nodes [ 0 ] . loadwallet , ' w8_symlink ' )
2018-04-18 17:32:16 -03:00
2018-06-28 13:36:28 -04:00
# Fail to load if a directory is specified that doesn't contain a wallet
os . mkdir ( wallet_dir ( ' empty_wallet_dir ' ) )
2023-06-14 06:28:54 -04:00
path = wallet_dir ( " empty_wallet_dir " )
2020-08-04 20:58:43 -04:00
assert_raises_rpc_error ( - 18 , " Wallet file verification failed. Failed to load database path ' {} ' . Data is not in recognized format. " . format ( path ) , self . nodes [ 0 ] . loadwallet , ' empty_wallet_dir ' )
2018-06-28 13:36:28 -04:00
2018-04-23 14:11:53 -03:00
self . log . info ( " Test dynamic wallet creation. " )
# Fail to create a wallet if it already exists.
2023-06-14 06:28:54 -04:00
path = wallet_dir ( " w2 " )
2020-08-04 21:03:27 -04:00
assert_raises_rpc_error ( - 4 , " Failed to create database path ' {} ' . Database already exists. " . format ( path ) , self . nodes [ 0 ] . createwallet , ' w2 ' )
2018-04-23 14:11:53 -03:00
# Successfully create a wallet with a new name
loadwallet_name = self . nodes [ 0 ] . createwallet ( ' w9 ' )
2020-10-29 15:51:32 -03:00
in_wallet_dir . append ( ' w9 ' )
2018-04-23 14:11:53 -03:00
assert_equal ( loadwallet_name [ ' name ' ] , ' w9 ' )
w9 = node . get_wallet_rpc ( ' w9 ' )
assert_equal ( w9 . getwalletinfo ( ) [ ' walletname ' ] , ' w9 ' )
assert ' w9 ' in self . nodes [ 0 ] . 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 = self . nodes [ 0 ] . createwallet ( new_wallet_name )
assert_equal ( loadwallet_name [ ' name ' ] , new_wallet_name )
w10 = node . get_wallet_rpc ( new_wallet_name )
assert_equal ( w10 . getwalletinfo ( ) [ ' walletname ' ] , new_wallet_name )
assert new_wallet_name in self . nodes [ 0 ] . listwallets ( )
2018-06-01 18:24:29 -04:00
self . log . info ( " Test dynamic wallet unloading " )
# Test `unloadwallet` errors
2022-09-14 12:13:58 -03:00
assert_raises_rpc_error ( - 3 , " JSON value of type null is not of expected type string " , self . nodes [ 0 ] . unloadwallet )
2018-06-01 18:24:29 -04:00
assert_raises_rpc_error ( - 18 , " Requested wallet does not exist or is not loaded " , self . nodes [ 0 ] . unloadwallet , " dummy " )
assert_raises_rpc_error ( - 18 , " Requested wallet does not exist or is not loaded " , node . get_wallet_rpc ( " dummy " ) . unloadwallet )
2020-11-21 15:57:22 -03:00
assert_raises_rpc_error ( - 8 , " RPC endpoint wallet and wallet_name parameter specify different wallets " , w1 . unloadwallet , " w2 " ) ,
2018-06-01 18:24:29 -04:00
# Successfully unload the specified wallet name
self . nodes [ 0 ] . unloadwallet ( " w1 " )
assert ' w1 ' not in self . nodes [ 0 ] . listwallets ( )
2020-11-21 15:57:22 -03:00
# Unload w1 again, this time providing the wallet name twice
self . nodes [ 0 ] . loadwallet ( " w1 " )
assert ' w1 ' in self . nodes [ 0 ] . listwallets ( )
w1 . unloadwallet ( " w1 " )
assert ' w1 ' not in self . nodes [ 0 ] . listwallets ( )
2018-06-01 18:24:29 -04:00
# Successfully unload the wallet referenced by the request endpoint
2018-10-19 19:10:49 -03:00
# Also ensure unload works during walletpassphrase timeout
w2 . encryptwallet ( ' test ' )
w2 . walletpassphrase ( ' test ' , 1 )
2018-06-01 18:24:29 -04:00
w2 . unloadwallet ( )
2018-10-19 19:10:49 -03:00
time . sleep ( 1.1 )
2018-06-01 18:24:29 -04:00
assert ' w2 ' not in self . nodes [ 0 ] . listwallets ( )
# Successfully unload all wallets
for wallet_name in self . nodes [ 0 ] . listwallets ( ) :
self . nodes [ 0 ] . unloadwallet ( wallet_name )
assert_equal ( self . nodes [ 0 ] . listwallets ( ) , [ ] )
2020-10-07 22:36:44 -03:00
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) " , self . nodes [ 0 ] . getwalletinfo )
2018-06-01 18:24:29 -04:00
2018-06-04 18:15:03 -04:00
# Successfully load a previously unloaded wallet
self . nodes [ 0 ] . loadwallet ( ' w1 ' )
assert_equal ( self . nodes [ 0 ] . listwallets ( ) , [ ' w1 ' ] )
assert_equal ( w1 . getwalletinfo ( ) [ ' walletname ' ] , ' w1 ' )
2020-10-29 15:51:32 -03:00
assert_equal ( sorted ( map ( lambda w : w [ ' name ' ] , self . nodes [ 0 ] . listwalletdir ( ) [ ' wallets ' ] ) ) , sorted ( in_wallet_dir ) )
2018-09-24 19:56:32 -03:00
2018-07-15 05:17:50 -04:00
# Test backing up and restoring wallets
self . log . info ( " Test wallet backup " )
self . restart_node ( 0 , [ ' -nowallet ' ] )
for wallet_name in wallet_names :
self . nodes [ 0 ] . loadwallet ( wallet_name )
for wallet_name in wallet_names :
rpc = self . nodes [ 0 ] . get_wallet_rpc ( wallet_name )
addr = rpc . getnewaddress ( )
backup = os . path . join ( self . options . tmpdir , ' backup.dat ' )
2020-10-07 15:50:05 -03:00
if os . path . exists ( backup ) :
os . unlink ( backup )
2018-07-15 05:17:50 -04:00
rpc . backupwallet ( backup )
self . nodes [ 0 ] . unloadwallet ( wallet_name )
2020-10-07 15:50:05 -03:00
shutil . copyfile ( empty_created_wallet if wallet_name == self . default_wallet_name else empty_wallet , wallet_file ( wallet_name ) )
2018-07-15 05:17:50 -04:00
self . nodes [ 0 ] . loadwallet ( wallet_name )
assert_equal ( rpc . getaddressinfo ( addr ) [ ' ismine ' ] , False )
self . nodes [ 0 ] . unloadwallet ( wallet_name )
shutil . copyfile ( backup , wallet_file ( wallet_name ) )
self . nodes [ 0 ] . loadwallet ( wallet_name )
assert_equal ( rpc . getaddressinfo ( addr ) [ ' ismine ' ] , True )
2019-02-04 15:50:21 -03:00
# Test .walletlock file is closed
self . start_node ( 1 )
wallet = os . path . join ( self . options . tmpdir , ' my_wallet ' )
self . nodes [ 0 ] . createwallet ( wallet )
2020-10-07 15:50:05 -03:00
if self . options . descriptors :
assert_raises_rpc_error ( - 4 , " Unable to obtain an exclusive lock " , self . nodes [ 1 ] . loadwallet , wallet )
else :
assert_raises_rpc_error ( - 4 , " Error initializing wallet database environment " , self . nodes [ 1 ] . loadwallet , wallet )
2019-02-04 15:50:21 -03:00
self . nodes [ 0 ] . unloadwallet ( wallet )
self . nodes [ 1 ] . loadwallet ( wallet )
2020-11-05 09:52:34 -03:00
2017-07-13 11:49:46 -04:00
if __name__ == ' __main__ ' :
2024-07-16 17:05:14 -04:00
MultiWalletTest ( __file__ ) . main ( )