Merge pull request #5198 from kittywhiskers/sqlite_tests

test: adjust functional tests to account for SQLite-only builds, add CI run
This commit is contained in:
PastaPastaPasta 2023-02-17 14:22:11 -06:00 committed by GitHub
commit e50d96d06f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 212 additions and 129 deletions

View File

@ -251,6 +251,13 @@ linux64_cxx20-build:
variables:
BUILD_TARGET: linux64_cxx20
linux64_sqlite-build:
extends: .build-template
needs:
- x86_64-pc-linux-gnu-debug
variables:
BUILD_TARGET: linux64_sqlite
linux64_fuzz-build:
extends: .build-template
needs:
@ -312,6 +319,13 @@ linux64-test:
variables:
BUILD_TARGET: linux64
linux64_sqlite-test:
extends: .test-template
needs:
- linux64_sqlite-build
variables:
BUILD_TARGET: linux64_sqlite
linux64_tsan-test:
extends:
- .test-template

View File

@ -30,6 +30,8 @@ elif [ "$BUILD_TARGET" = "linux64_fuzz" ]; then
source ./ci/test/00_setup_env_native_fuzz.sh
elif [ "$BUILD_TARGET" = "linux64_cxx20" ]; then
source ./ci/test/00_setup_env_native_cxx20.sh
elif [ "$BUILD_TARGET" = "linux64_sqlite" ]; then
source ./ci/test/00_setup_env_native_sqlite.sh
elif [ "$BUILD_TARGET" = "linux64_nowallet" ]; then
source ./ci/test/00_setup_env_native_nowallet.sh
elif [ "$BUILD_TARGET" = "mac" ]; then

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
#
# Copyright (c) 2019 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.UTF-8
export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libdbus-1-dev libharfbuzz-dev"
export DEP_OPTS="NO_BDB=1 NO_UPNP=1 DEBUG=1"
export GOAL="install"
export BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports --with-sqlite --without-bdb LDFLAGS=-static-libstdc++"

View File

@ -40,7 +40,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
"-rescan=<mode>",
"-salvagewallet",
"-spendzeroconfchange",
"-upgradewallet",
"-wallet=<path>",
"-walletbackupsdir=<dir>",
"-walletbroadcast",

View File

@ -1465,36 +1465,13 @@
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="btn_upgradewallet">
<property name="minimumSize">
<size>
<width>180</width>
<height>23</height>
</size>
</property>
<property name="text">
<string>Upgrade wallet format</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_repair_upgrade">
<property name="text">
<string>-upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!)</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="btn_reindex">
<property name="text">
<string>Rebuild index</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="5" column="1">
<widget class="QLabel" name="label_repair_reindex">
<property name="text">
<string>-reindex: Rebuild block chain index from current blk000??.dat files.</string>
@ -1504,7 +1481,7 @@
</property>
</widget>
</item>
<item row="7" column="1">
<item row="6" column="1">
<spacer name="verticalSpacerRepair">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -60,7 +60,6 @@ const TrafficGraphData::GraphRange INITIAL_TRAFFIC_GRAPH_SETTING = TrafficGraphD
// Repair parameters
const QString RESCAN1("-rescan=1");
const QString RESCAN2("-rescan=2");
const QString UPGRADEWALLET("-upgradewallet");
const QString REINDEX("-reindex");
namespace {
@ -496,10 +495,8 @@ RPCConsole::RPCConsole(interfaces::Node& node, QWidget* parent, Qt::WindowFlags
// Disable wallet repair options that require a wallet (enable them later when a wallet is added)
ui->btn_rescan1->setEnabled(false);
ui->btn_rescan2->setEnabled(false);
ui->btn_upgradewallet->setEnabled(false);
connect(ui->btn_rescan1, &QPushButton::clicked, this, &RPCConsole::walletRescan1);
connect(ui->btn_rescan2, &QPushButton::clicked, this, &RPCConsole::walletRescan2);
connect(ui->btn_upgradewallet, &QPushButton::clicked, this, &RPCConsole::walletUpgrade);
connect(ui->btn_reindex, &QPushButton::clicked, this, &RPCConsole::walletReindex);
// Register RPC timer interface
@ -726,7 +723,6 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
// The only loaded wallet
ui->btn_rescan1->setEnabled(true);
ui->btn_rescan2->setEnabled(true);
ui->btn_upgradewallet->setEnabled(true);
QString wallet_path = QString::fromStdString(GetWalletDir().string() + QDir::separator().toLatin1());
QString wallet_name = walletModel->getWalletName().isEmpty() ? "wallet.dat" : walletModel->getWalletName();
ui->wallet_path->setText(wallet_path + wallet_name);
@ -736,7 +732,6 @@ void RPCConsole::addWallet(WalletModel * const walletModel)
// No wallet recovery for multiple loaded wallets
ui->btn_rescan1->setEnabled(false);
ui->btn_rescan2->setEnabled(false);
ui->btn_upgradewallet->setEnabled(false);
ui->wallet_path->clear();
}
}
@ -750,7 +745,6 @@ void RPCConsole::removeWallet(WalletModel * const walletModel)
// Back to the only loaded wallet
ui->btn_rescan1->setEnabled(true);
ui->btn_rescan2->setEnabled(true);
ui->btn_upgradewallet->setEnabled(true);
WalletModel* wallet_model = ui->WalletSelector->itemData(1).value<WalletModel*>();
QString wallet_path = QString::fromStdString(GetWalletDir().string() + QDir::separator().toLatin1());
QString wallet_name = wallet_model->getWalletName().isEmpty() ? "wallet.dat" : wallet_model->getWalletName();
@ -759,7 +753,6 @@ void RPCConsole::removeWallet(WalletModel * const walletModel)
// No wallet recovery for multiple loaded wallets
ui->btn_rescan1->setEnabled(false);
ui->btn_rescan2->setEnabled(false);
ui->btn_upgradewallet->setEnabled(false);
ui->wallet_path->clear();
}
}
@ -821,12 +814,6 @@ void RPCConsole::walletRescan2()
buildParameterlist(RESCAN2);
}
/** Restart wallet with "-upgradewallet" */
void RPCConsole::walletUpgrade()
{
buildParameterlist(UPGRADEWALLET);
}
/** Restart wallet with "-reindex" */
void RPCConsole::walletReindex()
{
@ -852,7 +839,6 @@ void RPCConsole::buildParameterlist(QString arg)
// Remove existing repair-options
args.removeAll(RESCAN1);
args.removeAll(RESCAN2);
args.removeAll(UPGRADEWALLET);
args.removeAll(REINDEX);
// Append repair parameter to command line.

View File

@ -108,7 +108,6 @@ public Q_SLOTS:
/** Wallet repair options */
void walletRescan1();
void walletRescan2();
void walletUpgrade();
void walletReindex();
/** Append the message to the message widget */

View File

@ -182,6 +182,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getspecialtxes", 3, "skip" },
{ "getspecialtxes", 4, "verbosity" },
{ "disconnectnode", 1, "nodeid" },
{ "upgradewallet", 0, "version" },
// Echo with conversion (For testing only)
{ "echojson", 0, "arg0" },
{ "echojson", 1, "arg1" },

View File

@ -62,7 +62,6 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-rescan=<mode>", "Rescan the block chain for missing wallet transactions on startup"
" (1 = start from wallet creation time, 2 = start from genesis block)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-wallet=<path>", "Specify wallet path to load at startup. Can be used multiple times to load multiple wallets. Path is to a directory containing wallet data and log files. If the path is not absolute, it is interpreted relative to <walletdir>. This only loads existing wallets and does not create new ones. For backwards compatibility this also accepts names of existing top-level data files in <walletdir>.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
argsman.AddArg("-walletbackupsdir=<dir>", "Specify full path to directory for automatic wallet backups (must exist)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
argsman.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
@ -129,8 +128,6 @@ bool WalletInit::ParameterInteraction() const
return InitError(_("You can not start a masternode with wallet enabled."));
}
const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1;
if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) {
LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__);
}
@ -146,12 +143,6 @@ bool WalletInit::ParameterInteraction() const
gArgs.ForceRemoveArg("rescan");
}
if (is_multiwallet) {
if (gArgs.GetBoolArg("-upgradewallet", false)) {
return InitError(strprintf(_("%s is only allowed with a single wallet file"), "-upgradewallet"));
}
}
if (gArgs.GetBoolArg("-sysperms", false))
return InitError(Untranslated("-sysperms is not allowed in combination with enabled wallet functionality"));

View File

@ -2748,7 +2748,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
RPCHelpMan{"loadwallet",
"\nLoads a wallet from a wallet file or directory."
"\nNote that all wallet command-line options used when starting dashd will be"
"\napplied to the new wallet (eg -upgradewallet, rescan, etc).\n",
"\napplied to the new wallet (eg, rescan, etc).\n",
{
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
{"load_on_startup", RPCArg::Type::BOOL, /* default */ "null", "Save wallet name to persistent settings and load on startup. True to add wallet to startup list, false to remove, null to leave unchanged."},
@ -4064,6 +4064,42 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
return result;
}
static UniValue upgradewallet(const JSONRPCRequest& request)
{
RPCHelpMan{"upgradewallet",
"\nUpgrade the wallet. Upgrades to the latest version if no version number is specified\n"
"New keys may be generated and a new wallet backup will need to be made.",
{
{"version", RPCArg::Type::NUM, /* default */ strprintf("%d", FEATURE_LATEST), "The version number to upgrade to. Default is the latest wallet version"}
},
RPCResults{},
RPCExamples{
HelpExampleCli("upgradewallet", "120200")
+ HelpExampleRpc("upgradewallet", "120200")
}
}.Check(request);
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
if (!wallet) return NullUniValue;
CWallet* const pwallet = wallet.get();
RPCTypeCheck(request.params, {UniValue::VNUM}, true);
EnsureWalletIsUnlocked(pwallet);
int version = 0;
if (!request.params[0].isNull()) {
version = request.params[0].get_int();
}
bilingual_str error;
std::vector<bilingual_str> warnings;
if (!pwallet->UpgradeWallet(version, error, warnings)) {
throw JSONRPCError(RPC_WALLET_ERROR, error.original);
}
return error.original;
}
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@ -4123,6 +4159,7 @@ static const CRPCCommand commands[] =
{ "wallet", "signmessage", &signmessage, {"address","message"} },
{ "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} },
{ "wallet", "unloadwallet", &unloadwallet, {"wallet_name", "load_on_startup"} },
{ "wallet", "upgradewallet", &upgradewallet, {"version"} },
{ "wallet", "upgradetohd", &upgradetohd, {"mnemonic", "mnemonicpassphrase", "walletpassphrase", "rescan"} },
{ "wallet", "walletlock", &walletlock, {} },
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} },

View File

@ -4306,27 +4306,9 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
}
}
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun))
{
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
auto nMinVersion = DEFAULT_USE_HD_WALLET ? FEATURE_LATEST : FEATURE_COMPRPUBKEY;
if (nMaxVersion == 0) // the -upgradewallet without argument case
{
walletInstance->WalletLogPrintf("Performing wallet upgrade to %i\n", nMinVersion);
nMaxVersion = FEATURE_LATEST;
walletInstance->SetMinVersion(nMinVersion); // permanently upgrade the wallet immediately
}
else
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
return unload_wallet(_("Cannot downgrade wallet"));
}
walletInstance->SetMaxVersion(nMaxVersion);
}
if (fFirstRun)
{
walletInstance->SetMaxVersion(FEATURE_LATEST);
walletInstance->SetWalletFlags(wallet_creation_flags, false);
if (!(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
// Create new HD chain
@ -4596,6 +4578,27 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
return walletInstance;
}
bool CWallet::UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
int nMaxVersion = version;
auto nMinVersion = DEFAULT_USE_HD_WALLET ? FEATURE_LATEST : FEATURE_COMPRPUBKEY;
if (nMaxVersion == 0) {
WalletLogPrintf("Performing wallet upgrade to %i\n", nMinVersion);
nMaxVersion = FEATURE_LATEST;
SetMinVersion(nMinVersion); // permanently upgrade the wallet immediately
} else {
WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
}
if (nMaxVersion < GetVersion()) {
error = Untranslated("Cannot downgrade wallet");
return false;
}
SetMaxVersion(nMaxVersion);
return true;
}
void CWallet::postInitProcess()
{
LOCK(cs_wallet);

View File

@ -1241,6 +1241,9 @@ public:
LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
};
/** Upgrade the wallet */
bool UpgradeWallet(int version, bilingual_str& error, std::vector<bilingual_str>& warnings);
/** Get last block processed height */
int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet)
{

View File

@ -16,6 +16,8 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py
[components]
# Which components are enabled. These are commented out by `configure` if they were disabled when running config.
@ENABLE_WALLET_TRUE@ENABLE_WALLET=true
@USE_SQLITE_TRUE@USE_SQLITE=true
@USE_BDB_TRUE@USE_BDB=true
@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true
@BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true

View File

@ -4,6 +4,8 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Check that it's not possible to start a second bitcoind instance using the same datadir or wallet."""
import os
import random
import string
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
@ -27,11 +29,14 @@ class FilelockTest(BitcoinTestFramework):
self.nodes[1].assert_start_raises_init_error(extra_args=['-datadir={}'.format(self.nodes[0].datadir), '-noserver'], expected_msg=expected_msg)
if self.is_wallet_compiled():
self.nodes[0].createwallet(self.default_wallet_name)
wallet_name = ''.join([random.choice(string.ascii_lowercase) for _ in range(6)])
self.nodes[0].createwallet(wallet_name=wallet_name)
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second dashd instance using the same wallet")
expected_msg = "Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another dashd?"
if self.is_bdb_compiled():
expected_msg = "Error: Error initializing wallet database environment"
self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + self.default_wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
self.nodes[1].assert_start_raises_init_error(extra_args=['-walletdir={}'.format(wallet_dir), '-wallet=' + wallet_name, '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
if __name__ == '__main__':
FilelockTest().main()

View File

@ -48,6 +48,12 @@ class NotificationsTest(BitcoinTestFramework):
super().setup_network()
def run_test(self):
if self.is_wallet_compiled():
# Make the wallets
# Ensures that node 0 and node 1 share the same wallet for the conflicting transaction tests below.
for i, name in enumerate(self.wallet_names):
self.nodes[i].createwallet(wallet_name=name, load_on_startup=True)
self.log.info("test -blocknotify")
block_count = 10
blocks = self.nodes[1].generatetoaddress(block_count, self.nodes[1].getnewaddress() if self.is_wallet_compiled() else ADDRESS_BCRT1_UNSPENDABLE)

View File

@ -23,6 +23,7 @@ class TestBitcoinCli(BitcoinTestFramework):
self.log.info("Compare responses from getwalletinfo RPC and `dash-cli getwalletinfo`")
if self.is_wallet_compiled():
self.nodes[0].createwallet(self.default_wallet_name)
cli_response = self.nodes[0].cli.getwalletinfo()
rpc_response = self.nodes[0].getwalletinfo()
assert_equal(cli_response, rpc_response)

View File

@ -120,7 +120,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.supports_cli = True
self.bind_to_localhost_only = True
self.parse_args()
self.default_wallet_name = ""
self.default_wallet_name = "default_wallet" if self.options.is_sqlite_only else ""
self.wallet_data_filename = "wallet.dat"
self.extra_args_from_options = []
# Optional list of wallet names that can be set in set_test_params to
@ -129,6 +129,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
# skipped. If list is truncated, wallet creation is skipped and keys
# are not imported.
self.wallet_names = None
# By default the wallet is not required. Set to true by skip_if_no_wallet().
# When False, we ignore wallet_names regardless of what it is.
self.requires_wallet = False
self.set_test_params()
assert self.wallet_names is None or len(self.wallet_names) <= self.num_nodes
if self.options.timeout_factor == 0 :
@ -203,12 +206,21 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
parser.add_argument("--randomseed", type=int,
help="set a random seed for deterministically reproducing a previous test run")
parser.add_argument('--timeout-factor', dest="timeout_factor", type=float, default=1.0, help='adjust test timeouts by a factor. Setting it to 0 disables all timeouts')
self.add_options(parser)
self.options = parser.parse_args()
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
self.config = config
# Passthrough SQLite-only availability check output as option
self.options.is_sqlite_only = self.is_sqlite_compiled() and not self.is_bdb_compiled()
# Running TestShell in a Jupyter notebook causes an additional -f argument
# To keep TestShell from failing with an "unrecognized argument" error, we add a dummy "-f" argument
# source: https://stackoverflow.com/questions/48796169/how-to-fix-ipykernel-launcher-py-error-unrecognized-arguments-in-jupyter/56349168#56349168
parser.add_argument("-f", "--fff", help="a dummy argument to fool ipython", default="1")
self.options = parser.parse_args()
def setup(self):
"""Call this method to start up the test framework object with options set."""
@ -224,9 +236,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
self.options.cachedir = os.path.abspath(self.options.cachedir)
config = configparser.ConfigParser()
config.read_file(open(self.options.configfile))
self.config = config
config = self.config
self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/dashd' + config["environment"]["EXEEXT"])
self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/dash-cli' + config["environment"]["EXEEXT"])
@ -398,7 +409,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
extra_args = self.extra_args
self.add_nodes(self.num_nodes, extra_args)
self.start_nodes()
if self.is_wallet_compiled():
if self.requires_wallet:
self.import_deterministic_coinbase_privkeys()
if not self.setup_clean_chain:
for n in self.nodes:
@ -866,9 +877,20 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
def skip_if_no_wallet(self):
"""Skip the running test if wallet has not been compiled."""
self.requires_wallet = True
if not self.is_wallet_compiled():
raise SkipTest("wallet has not been compiled.")
def skip_if_no_sqlite(self):
"""Skip the running test if sqlite has not been compiled."""
if not self.is_sqlite_compiled():
raise SkipTest("sqlite has not been compiled.")
def skip_if_no_bdb(self):
"""Skip the running test if BDB has not been compiled."""
if not self.is_bdb_compiled():
raise SkipTest("BDB has not been compiled.")
def skip_if_no_wallet_tool(self):
"""Skip the running test if dash-wallet has not been compiled."""
if not self.is_wallet_tool_compiled():
@ -895,6 +917,13 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
"""Checks whether the zmq module was compiled."""
return self.config["components"].getboolean("ENABLE_ZMQ")
def is_sqlite_compiled(self):
"""Checks whether the wallet module was compiled with Sqlite support."""
return self.config["components"].getboolean("USE_SQLITE")
def is_bdb_compiled(self):
"""Checks whether the wallet module was compiled with BDB support."""
return self.config["components"].getboolean("USE_BDB")
MASTERNODE_COLLATERAL = 1000
HIGHPERFORMANCE_MASTERNODE_COLLATERAL = 4000

View File

@ -70,8 +70,11 @@ class ToolWalletTest(BitcoinTestFramework):
self.assert_raises_tool_error('Error: two methods provided (info and create). Only one method should be provided.', 'info', 'create')
self.assert_raises_tool_error('Error parsing command line arguments: Invalid parameter -foo', '-foo')
locked_dir = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets")
error = "SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another dashd?"
if self.is_bdb_compiled():
error = 'Error initializing wallet database environment "{}"!'.format(locked_dir)
self.assert_raises_tool_error(
'Error initializing wallet database environment "{}"!'.format(locked_dir),
error,
'-wallet=' + self.default_wallet_name,
'info',
)
@ -103,7 +106,7 @@ class ToolWalletTest(BitcoinTestFramework):
Transactions: 0
Address Book: 1
''')
self.assert_tool_output(out, '-wallet=wallet.dat', 'info')
self.assert_tool_output(out, '-wallet=' + self.default_wallet_name, 'info')
self.start_node(0)
self.nodes[0].upgradetohd()
@ -233,6 +236,7 @@ class ToolWalletTest(BitcoinTestFramework):
self.test_tool_wallet_info_after_transaction()
self.test_tool_wallet_create_on_existing_wallet()
self.test_getwalletinfo_on_different_wallet()
if self.is_bdb_compiled():
self.test_salvage()
if __name__ == '__main__':

View File

@ -190,6 +190,7 @@ class WalletDumpTest(BitcoinTestFramework):
result = self.nodes[0].getaddressinfo(multisig_addr)
assert result['ismine'] == True
if self.is_bdb_compiled():
self.log.info('Check that wallet is flushed')
with self.nodes[0].assert_debug_log(['Flushing wallet.dat'], timeout=20):
self.nodes[0].getnewaddress()

View File

@ -11,6 +11,9 @@ class WalletRBFTest(BitcoinTestFramework):
self.num_nodes = 1
self.setup_clean_chain = True
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
self.nodes[0].generate(101)

View File

@ -29,7 +29,7 @@ class WalletHDTest(BitcoinTestFramework):
def run_test(self):
# Make sure can't switch off usehd after wallet creation
self.stop_node(1)
self.nodes[1].assert_start_raises_init_error(['-usehd=0'], "Error: Error loading : You can't disable HD on an already existing HD wallet")
self.nodes[1].assert_start_raises_init_error(['-usehd=0'], "Error: Error loading %s: You can't disable HD on an already existing HD wallet" % self.default_wallet_name)
self.start_node(1)
self.connect_nodes(0, 1)

View File

@ -24,6 +24,7 @@ from test_framework.util import (
FEATURE_LATEST = 120200
got_loading_error = False
def test_load_unload(node, name):
global got_loading_error
for i in range(10):
@ -37,7 +38,6 @@ def test_load_unload(node, name):
got_loading_error = True
return
class MultiWalletTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
@ -98,23 +98,32 @@ class MultiWalletTest(BitcoinTestFramework):
# 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
# 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
wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', self.default_wallet_name]
to_create = ['w1', 'w2', 'w3', 'w', 'sub/w5', 'w7_symlink']
in_wallet_dir = to_create.copy() # 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]
if not self.options.is_sqlite_only:
to_load.append('w8')
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
if os.name == 'nt':
wallet_names.remove('w7_symlink')
self.start_node(0)
for wallet_name in wallet_names[:-2]:
for wallet_name in to_create:
self.nodes[0].createwallet(wallet_name)
for wallet_name in wallet_names[-2:]:
for wallet_name in to_load:
self.nodes[0].loadwallet(wallet_name)
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8'])
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), sorted(in_wallet_dir))
assert_equal(set(node.listwallets()), set(wallet_names))
# should raise rpc error if wallet path can't be created
assert_raises_rpc_error(-1, "boost::filesystem::create_directory:", self.nodes[0].createwallet, "w8/bad")
err_code = -4 if self.options.is_sqlite_only else -1
assert_raises_rpc_error(err_code, "boost::filesystem::create_directory:", self.nodes[0].createwallet, "w8/bad")
# check that all requested wallets were created
self.stop_node(0)
@ -128,8 +137,11 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(0, ['-wallet=w1', '-wallet=w1'])
self.stop_node(0, 'Warning: Ignoring duplicate -wallet w1.')
if not self.options.is_sqlite_only:
# 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"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)
@ -145,10 +157,6 @@ class MultiWalletTest(BitcoinTestFramework):
open(not_a_dir, 'a', encoding="utf8").close()
self.nodes[0].assert_start_raises_init_error(['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory')
self.log.info("Do not allow -upgradewallet with multiwallet")
self.nodes[0].assert_start_raises_init_error(['-upgradewallet', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
self.nodes[0].assert_start_raises_init_error(['-upgradewallet=1', '-wallet=w1', '-wallet=w2'], "Error: -upgradewallet is only allowed with a single wallet file")
# if wallets/ doesn't exist, datadir should be the default wallet dir
wallet_dir2 = data_dir('walletdir')
os.rename(wallet_dir(), wallet_dir2)
@ -173,14 +181,17 @@ class MultiWalletTest(BitcoinTestFramework):
os.mkdir(competing_wallet_dir)
self.restart_node(0, ['-nowallet', '-walletdir=' + competing_wallet_dir])
self.nodes[0].createwallet(self.default_wallet_name)
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\"!"
if self.options.is_sqlite_only:
exp_stderr = r"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another dashd?"
else:
exp_stderr = r"Error: Error initializing wallet database environment \"\S+competing_walletdir\S*\"!"
self.nodes[1].assert_start_raises_init_error(['-walletdir=' + competing_wallet_dir], exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
self.restart_node(0)
for wallet_name in wallet_names:
self.nodes[0].loadwallet(wallet_name)
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy'])
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), sorted(in_wallet_dir))
wallets = [wallet(w) for w in wallet_names]
wallet_bad = wallet("bad")
@ -271,19 +282,23 @@ class MultiWalletTest(BitcoinTestFramework):
# Fail to load duplicate wallets
path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", "w1", self.wallet_data_filename)
if self.options.is_sqlite_only:
assert_raises_rpc_error(-4, "Wallet file verification failed. SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another dashd?", self.nodes[0].loadwallet, wallet_names[0])
else:
assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, wallet_names[0])
# 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)
path = os.path.join(self.options.tmpdir, "node0", "regtest", "wallets", self.wallet_data_filename)
assert_raises_rpc_error(-4, "Wallet file verification failed. Refusing to load database. Data file '{}' is already loaded.".format(path), self.nodes[0].loadwallet, self.wallet_data_filename)
# 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')
# 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')
# Fail to load if wallet file is a symlink
if os.name != 'nt':
assert_raises_rpc_error(-4, "Wallet file verification failed. Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink')
@ -296,6 +311,7 @@ class MultiWalletTest(BitcoinTestFramework):
# Successfully create a wallet with a new name
loadwallet_name = self.nodes[0].createwallet('w9')
in_wallet_dir.append('w9')
assert_equal(loadwallet_name['name'], 'w9')
w9 = node.get_wallet_rpc('w9')
assert_equal(w9.getwalletinfo()['walletname'], 'w9')
@ -348,7 +364,7 @@ class MultiWalletTest(BitcoinTestFramework):
assert_equal(self.nodes[0].listwallets(), ['w1'])
assert_equal(w1.getwalletinfo()['walletname'], 'w1')
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), [self.default_wallet_name, os.path.join('sub', 'w5'), 'w', 'w1', 'w2', 'w3', 'w7', 'w7_symlink', 'w8', 'w8_copy', 'w9'])
assert_equal(sorted(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), sorted(in_wallet_dir))
# Test backing up and restoring wallets
self.log.info("Test wallet backup")
@ -373,22 +389,13 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(1)
wallet = os.path.join(self.options.tmpdir, 'my_wallet')
self.nodes[0].createwallet(wallet)
assert_raises_rpc_error(-4, "Error initializing wallet database environment", self.nodes[1].loadwallet, wallet)
if self.options.is_sqlite_only:
exp_stderr = "SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another dashd?"
else:
exp_stderr = "Error initializing wallet database environment"
assert_raises_rpc_error(-4, exp_stderr, self.nodes[1].loadwallet, wallet)
self.nodes[0].unloadwallet(wallet)
self.nodes[1].loadwallet(wallet)
# Fail to load if wallet is downgraded
shutil.copytree(os.path.join(self.options.data_wallets_dir, 'high_minversion'), wallet_dir('high_minversion'))
self.restart_node(0, extra_args=['-upgradewallet={}'.format(FEATURE_LATEST)])
assert {'name': 'high_minversion'} in self.nodes[0].listwalletdir()['wallets']
self.log.info("Fail -upgradewallet that results in downgrade")
assert_raises_rpc_error(
-4,
'Wallet loading failed. Error loading {}: Wallet requires newer version of {}'.format(
wallet_dir('high_minversion', 'wallet.dat'), "Dash Core"),
lambda: self.nodes[0].loadwallet(filename='high_minversion'),
)
if __name__ == '__main__':
MultiWalletTest().main()

View File

@ -34,7 +34,7 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
self.log.info("Recover non-HD wallet to check different upgrade paths")
node = self.nodes[0]
self.stop_node(0)
shutil.copyfile(os.path.join(node.datadir, "non_hd.bak"), os.path.join(node.datadir, "regtest", "wallet.dat"))
shutil.copyfile(os.path.join(node.datadir, "non_hd.bak"), os.path.join(node.datadir, self.chain, self.default_wallet_name, self.wallet_data_filename))
self.start_node(0)
assert 'hdchainid' not in node.getwalletinfo()
@ -68,7 +68,7 @@ class WalletUpgradeToHDTest(BitcoinTestFramework):
self.log.info("Should no longer be able to start it with HD disabled")
self.stop_node(0)
node.assert_start_raises_init_error(['-usehd=0'], "Error: Error loading : You can't disable HD on an already existing HD wallet")
node.assert_start_raises_init_error(['-usehd=0'], "Error: Error loading %s: You can't disable HD on an already existing HD wallet" % self.default_wallet_name)
self.start_node(0)
balance_after = node.getbalance()

View File

@ -32,6 +32,7 @@ class UpgradeWalletTest(BitcoinTestFramework):
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
self.skip_if_no_bdb()
def setup_network(self):
self.setup_nodes()