Merge bitcoin/bitcoin#23732: refactor: Remove gArgs from bdb.h and sqlite.h

39b1763730177cd7d6a32fd9321da640b0d65e0e Replace use of `ArgsManager` with `DatabaseOptions` (Kiminuo)

Pull request description:

  Contributes to #21005.

  The goal of this PR is to remove `gArgs` from database classes (i.e. `bdb.h` and `sqlite.h`) so that they can be tested without relying on `gArgs` in tests.

  Notes:

  * My goal is to enable unit-testing without relying on `gArgs` as much as possible. Global variables are hard to reason about which in turn makes it slightly harder to contribute to this codebase. When the compiler does the heavy lifting for us and allows us only to construct an object (or call a method) with valid parameters, we may also save some time in code reviews. The cost for this is passing an argument which is not for free but the cost is very miniscule compared to benefits, I think.
      * GUI code is an exception because it seems fine to have `gArgs` there so I don't plan to make changes in `src/qt` folder, for example.
  * My approach to removal of `gArgs` uses is moving from lower levels to upper ones and pass `ArgsManager` as an argument as needed. The approach is very similar to what #20158.

ACKs for top commit:
  achow101:
    ACK 39b1763730177cd7d6a32fd9321da640b0d65e0e
  ryanofsky:
    Code review ACK 39b1763730177cd7d6a32fd9321da640b0d65e0e. Just the two small ReadDatabaseArgs and Berkeley open changes that were discussed since the last review

Tree-SHA512: aa066b314db593e46c18698fe8cdd500f558b405dc04e4a9a3ff57b52b5b3a81a6cb090e0e661785d1d02c1bf18958c1f4cd715ff233aab63381e3f80960622d
This commit is contained in:
MarcoFalke 2022-03-24 07:40:27 +01:00
commit 98e9d8e8e2
No known key found for this signature in database
GPG Key ID: CE2B75697E69A548
21 changed files with 93 additions and 60 deletions

View File

@ -60,12 +60,12 @@ bool WalletDatabaseFileId::operator==(const WalletDatabaseFileId& rhs) const
* erases the weak pointer from the g_dbenvs map. * erases the weak pointer from the g_dbenvs map.
* @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map. * @post A new BerkeleyEnvironment weak pointer is inserted into g_dbenvs if the directory path key was not already in the map.
*/ */
std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory) std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory)
{ {
LOCK(cs_db); LOCK(cs_db);
auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>()); auto inserted = g_dbenvs.emplace(fs::PathToString(env_directory), std::weak_ptr<BerkeleyEnvironment>());
if (inserted.second) { if (inserted.second) {
auto env = std::make_shared<BerkeleyEnvironment>(env_directory); auto env = std::make_shared<BerkeleyEnvironment>(env_directory, use_shared_memory);
inserted.first->second = env; inserted.first->second = env;
return env; return env;
} }
@ -113,7 +113,7 @@ void BerkeleyEnvironment::Reset()
fMockDb = false; fMockDb = false;
} }
BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path) : strPath(fs::PathToString(dir_path)) BerkeleyEnvironment::BerkeleyEnvironment(const fs::path& dir_path, bool use_shared_memory) : strPath(fs::PathToString(dir_path)), m_use_shared_memory(use_shared_memory)
{ {
Reset(); Reset();
} }
@ -145,8 +145,9 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile)); LogPrintf("BerkeleyEnvironment::Open: LogDir=%s ErrorFile=%s\n", fs::PathToString(pathLogDir), fs::PathToString(pathErrorFile));
unsigned int nEnvFlags = 0; unsigned int nEnvFlags = 0;
if (gArgs.GetBoolArg("-privdb", DEFAULT_WALLET_PRIVDB)) if (!m_use_shared_memory) {
nEnvFlags |= DB_PRIVATE; nEnvFlags |= DB_PRIVATE;
}
dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str()); dbenv->set_lg_dir(fs::PathToString(pathLogDir).c_str());
dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet dbenv->set_cachesize(0, 0x100000, 1); // 1 MiB should be enough for just the wallet
@ -188,7 +189,7 @@ bool BerkeleyEnvironment::Open(bilingual_str& err)
} }
//! Construct an in-memory mock Berkeley environment for testing //! Construct an in-memory mock Berkeley environment for testing
BerkeleyEnvironment::BerkeleyEnvironment() BerkeleyEnvironment::BerkeleyEnvironment() : m_use_shared_memory(false)
{ {
Reset(); Reset();
@ -377,7 +378,7 @@ void BerkeleyBatch::Flush()
nMinutes = 1; nMinutes = 1;
if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault if (env) { // env is nullptr for dummy databases (i.e. in tests). Don't actually flush if env is nullptr so we don't segfault
env->dbenv->txn_checkpoint(nMinutes ? gArgs.GetIntArg("-dblogsize", DEFAULT_WALLET_DBLOGSIZE) * 1024 : 0, nMinutes, 0); env->dbenv->txn_checkpoint(nMinutes ? m_database.m_max_log_mb * 1024 : 0, nMinutes, 0);
} }
} }
@ -831,13 +832,13 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con
{ {
LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor LOCK(cs_db); // Lock env.m_databases until insert in BerkeleyDatabase constructor
std::string data_filename = fs::PathToString(data_file.filename()); std::string data_filename = fs::PathToString(data_file.filename());
std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path()); std::shared_ptr<BerkeleyEnvironment> env = GetBerkeleyEnv(data_file.parent_path(), options.use_shared_memory);
if (env->m_databases.count(data_filename)) { if (env->m_databases.count(data_filename)) {
error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename))); error = Untranslated(strprintf("Refusing to load database. Data file '%s' is already loaded.", fs::PathToString(env->Directory() / data_filename)));
status = DatabaseStatus::FAILED_ALREADY_LOADED; status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr; return nullptr;
} }
db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename)); db = std::make_unique<BerkeleyDatabase>(std::move(env), std::move(data_filename), options);
} }
if (options.verify && !db->Verify(error)) { if (options.verify && !db->Verify(error)) {

View File

@ -32,8 +32,6 @@
struct bilingual_str; struct bilingual_str;
namespace wallet { namespace wallet {
static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
static const bool DEFAULT_WALLET_PRIVDB = true;
struct WalletDatabaseFileId { struct WalletDatabaseFileId {
u_int8_t value[DB_FILE_ID_LEN]; u_int8_t value[DB_FILE_ID_LEN];
@ -56,8 +54,9 @@ public:
std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases; std::map<std::string, std::reference_wrapper<BerkeleyDatabase>> m_databases;
std::unordered_map<std::string, WalletDatabaseFileId> m_fileids; std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
std::condition_variable_any m_db_in_use; std::condition_variable_any m_db_in_use;
bool m_use_shared_memory;
explicit BerkeleyEnvironment(const fs::path& env_directory); explicit BerkeleyEnvironment(const fs::path& env_directory, bool use_shared_memory);
BerkeleyEnvironment(); BerkeleyEnvironment();
~BerkeleyEnvironment(); ~BerkeleyEnvironment();
void Reset(); void Reset();
@ -85,7 +84,7 @@ public:
}; };
/** Get BerkeleyEnvironment given a directory path. */ /** Get BerkeleyEnvironment given a directory path. */
std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory); std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory);
class BerkeleyBatch; class BerkeleyBatch;
@ -98,8 +97,8 @@ public:
BerkeleyDatabase() = delete; BerkeleyDatabase() = delete;
/** Create DB handle to real database */ /** Create DB handle to real database */
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) : BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename, const DatabaseOptions& options) :
WalletDatabase(), env(std::move(env)), strFile(std::move(filename)) WalletDatabase(), env(std::move(env)), strFile(std::move(filename)), m_max_log_mb(options.max_log_mb)
{ {
auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this)); auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
assert(inserted.second); assert(inserted.second);
@ -160,6 +159,7 @@ public:
std::unique_ptr<Db> m_db; std::unique_ptr<Db> m_db;
std::string strFile; std::string strFile;
int64_t m_max_log_mb;
/** Make a BerkeleyBatch connected to this database */ /** Make a BerkeleyBatch connected to this database */
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override; std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;

View File

@ -6,6 +6,7 @@
#include <chainparams.h> #include <chainparams.h>
#include <fs.h> #include <fs.h>
#include <logging.h> #include <logging.h>
#include <util/system.h>
#include <wallet/db.h> #include <wallet/db.h>
#include <exception> #include <exception>
@ -137,4 +138,13 @@ bool IsSQLiteFile(const fs::path& path)
// Check the application id matches our network magic // Check the application id matches our network magic
return memcmp(Params().MessageStart(), app_id, 4) == 0; return memcmp(Params().MessageStart(), app_id, 4) == 0;
} }
void ReadDatabaseArgs(const ArgsManager& args, DatabaseOptions& options)
{
// Override current options with args values, if any were specified
options.use_unsafe_sync = args.GetBoolArg("-unsafesqlitesync", options.use_unsafe_sync);
options.use_shared_memory = !args.GetBoolArg("-privdb", !options.use_shared_memory);
options.max_log_mb = args.GetIntArg("-dblogsize", options.max_log_mb);
}
} // namespace wallet } // namespace wallet

View File

@ -16,6 +16,7 @@
#include <optional> #include <optional>
#include <string> #include <string>
class ArgsManager;
struct bilingual_str; struct bilingual_str;
namespace wallet { namespace wallet {
@ -207,7 +208,12 @@ struct DatabaseOptions {
std::optional<DatabaseFormat> require_format; std::optional<DatabaseFormat> require_format;
uint64_t create_flags = 0; uint64_t create_flags = 0;
SecureString create_passphrase; SecureString create_passphrase;
bool verify = true;
// Specialized options. Not every option is supported by every backend.
bool verify = true; //!< Check data integrity on load.
bool use_unsafe_sync = false; //!< Disable file sync for faster performance.
bool use_shared_memory = false; //!< Let other processes access the database.
int64_t max_log_mb = 100; //!< Max log size to allow before consolidating.
}; };
enum class DatabaseStatus { enum class DatabaseStatus {
@ -227,6 +233,7 @@ enum class DatabaseStatus {
/** Recursively list database paths in directory. */ /** Recursively list database paths in directory. */
std::vector<fs::path> ListDatabases(const fs::path& path); std::vector<fs::path> ListDatabases(const fs::path& path);
void ReadDatabaseArgs(const ArgsManager& args, DatabaseOptions& options);
std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
fs::path BDBDataFile(const fs::path& path); fs::path BDBDataFile(const fs::path& path);

View File

@ -19,10 +19,10 @@ namespace wallet {
static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP"; static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP";
uint32_t DUMP_VERSION = 1; uint32_t DUMP_VERSION = 1;
bool DumpWallet(CWallet& wallet, bilingual_str& error) bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error)
{ {
// Get the dumpfile // Get the dumpfile
std::string dump_filename = gArgs.GetArg("-dumpfile", ""); std::string dump_filename = args.GetArg("-dumpfile", "");
if (dump_filename.empty()) { if (dump_filename.empty()) {
error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided."); error = _("No dump file provided. To use dump, -dumpfile=<filename> must be provided.");
return false; return false;
@ -114,10 +114,10 @@ static void WalletToolReleaseWallet(CWallet* wallet)
delete wallet; delete wallet;
} }
bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings) bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
// Get the dumpfile // Get the dumpfile
std::string dump_filename = gArgs.GetArg("-dumpfile", ""); std::string dump_filename = args.GetArg("-dumpfile", "");
if (dump_filename.empty()) { if (dump_filename.empty()) {
error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided."); error = _("No dump file provided. To use createfromdump, -dumpfile=<filename> must be provided.");
return false; return false;
@ -171,7 +171,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
return false; return false;
} }
// Get the data file format with format_value as the default // Get the data file format with format_value as the default
std::string file_format = gArgs.GetArg("-format", format_value); std::string file_format = args.GetArg("-format", format_value);
if (file_format.empty()) { if (file_format.empty()) {
error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided."); error = _("No wallet file format provided. To use createfromdump, -format=<format> must be provided.");
return false; return false;
@ -193,6 +193,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(args, options);
options.require_create = true; options.require_create = true;
options.require_format = data_format; options.require_format = data_format;
std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error); std::unique_ptr<WalletDatabase> database = MakeDatabase(wallet_path, options, status, error);

View File

@ -11,11 +11,12 @@
#include <vector> #include <vector>
struct bilingual_str; struct bilingual_str;
class ArgsManager;
namespace wallet { namespace wallet {
class CWallet; class CWallet;
bool DumpWallet(CWallet& wallet, bilingual_str& error); bool DumpWallet(const ArgsManager& args, CWallet& wallet, bilingual_str& error);
bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings); bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings);
} // namespace wallet } // namespace wallet
#endif // BITCOIN_WALLET_DUMP_H #endif // BITCOIN_WALLET_DUMP_H

View File

@ -80,9 +80,9 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const
argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); argsman.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#ifdef USE_BDB #ifdef USE_BDB
argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DatabaseOptions().max_log_mb), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST); argsman.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", !DatabaseOptions().use_shared_memory), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::WALLET_DEBUG_TEST);
#else #else
argsman.AddHiddenArgs({"-dblogsize", "-flushwallet", "-privdb"}); argsman.AddHiddenArgs({"-dblogsize", "-flushwallet", "-privdb"});
#endif #endif

View File

@ -544,6 +544,7 @@ public:
std::shared_ptr<CWallet> wallet; std::shared_ptr<CWallet> wallet;
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(*m_context.args, options);
options.require_create = true; options.require_create = true;
options.create_flags = wallet_creation_flags; options.create_flags = wallet_creation_flags;
options.create_passphrase = passphrase; options.create_passphrase = passphrase;
@ -553,6 +554,7 @@ public:
{ {
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(*m_context.args, options);
options.require_existing = true; options.require_existing = true;
return MakeWallet(m_context, LoadWallet(m_context, name, true /* load_on_start */, options, status, error, warnings)); return MakeWallet(m_context, LoadWallet(m_context, name, true /* load_on_start */, options, status, error, warnings));
} }

View File

@ -57,6 +57,7 @@ bool VerifyWallets(WalletContext& context)
if (!args.IsArgSet("wallet")) { if (!args.IsArgSet("wallet")) {
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(args, options);
bilingual_str error_string; bilingual_str error_string;
options.require_existing = true; options.require_existing = true;
options.verify = false; options.verify = false;
@ -84,6 +85,7 @@ bool VerifyWallets(WalletContext& context)
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(args, options);
options.require_existing = true; options.require_existing = true;
options.verify = true; options.verify = true;
bilingual_str error_string; bilingual_str error_string;
@ -112,6 +114,7 @@ bool LoadWallets(WalletContext& context)
} }
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(*context.args, options);
options.require_existing = true; options.require_existing = true;
options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets() options.verify = false; // No need to verify, assuming verified earlier in VerifyWallets()
bilingual_str error; bilingual_str error;

View File

@ -8,6 +8,7 @@
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h> #include <rpc/util.h>
#include <util/translation.h> #include <util/translation.h>
#include <wallet/context.h>
#include <wallet/receive.h> #include <wallet/receive.h>
#include <wallet/rpc/wallet.h> #include <wallet/rpc/wallet.h>
#include <wallet/rpc/util.h> #include <wallet/rpc/util.h>
@ -220,6 +221,7 @@ static RPCHelpMan loadwallet()
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(*context.args, options);
options.require_existing = true; options.require_existing = true;
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
@ -381,6 +383,7 @@ static RPCHelpMan createwallet()
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(*context.args, options);
options.require_create = true; options.require_create = true;
options.create_flags = flags; options.create_flags = flags;
options.create_passphrase = passphrase; options.create_passphrase = passphrase;

View File

@ -23,10 +23,11 @@ static bool KeyFilter(const std::string& type)
return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN; return WalletBatch::IsKeyType(type) || type == DBKeys::HDCHAIN;
} }
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings) bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
DatabaseOptions options; DatabaseOptions options;
DatabaseStatus status; DatabaseStatus status;
ReadDatabaseArgs(args, options);
options.require_existing = true; options.require_existing = true;
options.verify = false; options.verify = false;
options.require_format = DatabaseFormat::BERKELEY; options.require_format = DatabaseFormat::BERKELEY;

View File

@ -9,10 +9,11 @@
#include <fs.h> #include <fs.h>
#include <streams.h> #include <streams.h>
class ArgsManager;
struct bilingual_str; struct bilingual_str;
namespace wallet { namespace wallet {
bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings); bool RecoverDatabaseFile(const ArgsManager& args, const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings);
} // namespace wallet } // namespace wallet
#endif // BITCOIN_WALLET_SALVAGE_H #endif // BITCOIN_WALLET_SALVAGE_H

View File

@ -83,8 +83,8 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va
} }
} }
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock) SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock)
: WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path)) : WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path)), m_use_unsafe_sync(options.use_unsafe_sync)
{ {
{ {
LOCK(g_sqlite_mutex); LOCK(g_sqlite_mutex);
@ -255,7 +255,7 @@ void SQLiteDatabase::Open()
// Enable fullfsync for the platforms that use it // Enable fullfsync for the platforms that use it
SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync"); SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync");
if (gArgs.GetBoolArg("-unsafesqlitesync", false)) { if (m_use_unsafe_sync) {
// Use normal synchronous mode for the journal // Use normal synchronous mode for the journal
LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n"); LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n");
SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF"); SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF");
@ -546,7 +546,7 @@ std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const D
{ {
try { try {
fs::path data_file = SQLiteDataFile(path); fs::path data_file = SQLiteDataFile(path);
auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file); auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options);
if (options.verify && !db->Verify(error)) { if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY; status = DatabaseStatus::FAILED_VERIFY;
return nullptr; return nullptr;

View File

@ -69,7 +69,7 @@ public:
SQLiteDatabase() = delete; SQLiteDatabase() = delete;
/** Create DB handle to real database */ /** Create DB handle to real database */
SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, bool mock = false); SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock = false);
~SQLiteDatabase(); ~SQLiteDatabase();
@ -113,6 +113,7 @@ public:
std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override; std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
sqlite3* m_db{nullptr}; sqlite3* m_db{nullptr};
bool m_use_unsafe_sync;
}; };
std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);

View File

@ -19,13 +19,13 @@ static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, s
{ {
fs::path data_file = BDBDataFile(path); fs::path data_file = BDBDataFile(path);
database_filename = fs::PathToString(data_file.filename()); database_filename = fs::PathToString(data_file.filename());
return GetBerkeleyEnv(data_file.parent_path()); return GetBerkeleyEnv(data_file.parent_path(), false);
} }
BOOST_AUTO_TEST_CASE(getwalletenv_file) BOOST_AUTO_TEST_CASE(getwalletenv_file)
{ {
std::string test_name = "test_name.dat"; std::string test_name = "test_name.dat";
const fs::path datadir = gArgs.GetDataDirNet(); const fs::path datadir = m_args.GetDataDirNet();
fs::path file_path = datadir / test_name; fs::path file_path = datadir / test_name;
std::ofstream f{file_path}; std::ofstream f{file_path};
f.close(); f.close();
@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file)
BOOST_AUTO_TEST_CASE(getwalletenv_directory) BOOST_AUTO_TEST_CASE(getwalletenv_directory)
{ {
std::string expected_name = "wallet.dat"; std::string expected_name = "wallet.dat";
const fs::path datadir = gArgs.GetDataDirNet(); const fs::path datadir = m_args.GetDataDirNet();
std::string filename; std::string filename;
std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename); std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(datadir, filename);
@ -49,8 +49,8 @@ BOOST_AUTO_TEST_CASE(getwalletenv_directory)
BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple) BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_multiple)
{ {
fs::path datadir = gArgs.GetDataDirNet() / "1"; fs::path datadir = m_args.GetDataDirNet() / "1";
fs::path datadir_2 = gArgs.GetDataDirNet() / "2"; fs::path datadir_2 = m_args.GetDataDirNet() / "2";
std::string filename; std::string filename;
std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename); std::shared_ptr<BerkeleyEnvironment> env_1 = GetWalletEnv(datadir, filename);

View File

@ -15,12 +15,12 @@
namespace wallet { namespace wallet {
InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
{ {
m_wallet_loader = MakeWalletLoader(*m_node.chain, *Assert(m_node.args)); m_wallet_loader = MakeWalletLoader(*m_node.chain, m_args);
std::string sep; std::string sep;
sep += fs::path::preferred_separator; sep += fs::path::preferred_separator;
m_datadir = gArgs.GetDataDirNet(); m_datadir = m_args.GetDataDirNet();
m_cwd = fs::current_path(); m_cwd = fs::current_path();
m_walletdir_path_cases["default"] = m_datadir / "wallets"; m_walletdir_path_cases["default"] = m_datadir / "wallets";
@ -42,14 +42,11 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam
InitWalletDirTestingSetup::~InitWalletDirTestingSetup() InitWalletDirTestingSetup::~InitWalletDirTestingSetup()
{ {
gArgs.LockSettings([&](util::Settings& settings) {
settings.forced_settings.erase("walletdir");
});
fs::current_path(m_cwd); fs::current_path(m_cwd);
} }
void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path) void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path)
{ {
gArgs.ForceSetArg("-walletdir", fs::PathToString(walletdir_path)); m_args.ForceSetArg("-walletdir", fs::PathToString(walletdir_path));
} }
} // namespace wallet } // namespace wallet

View File

@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default)
SetWalletDir(m_walletdir_path_cases["default"]); SetWalletDir(m_walletdir_path_cases["default"]);
bool result = m_wallet_loader->verify(); bool result = m_wallet_loader->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path walletdir = m_args.GetPathArg("-walletdir");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(walletdir, expected_path);
} }
@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom)
SetWalletDir(m_walletdir_path_cases["custom"]); SetWalletDir(m_walletdir_path_cases["custom"]);
bool result = m_wallet_loader->verify(); bool result = m_wallet_loader->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path walletdir = m_args.GetPathArg("-walletdir");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]);
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(walletdir, expected_path);
} }
@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing)
SetWalletDir(m_walletdir_path_cases["trailing"]); SetWalletDir(m_walletdir_path_cases["trailing"]);
bool result = m_wallet_loader->verify(); bool result = m_wallet_loader->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path walletdir = m_args.GetPathArg("-walletdir");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(walletdir, expected_path);
} }
@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2)
SetWalletDir(m_walletdir_path_cases["trailing2"]); SetWalletDir(m_walletdir_path_cases["trailing2"]);
bool result = m_wallet_loader->verify(); bool result = m_wallet_loader->verify();
BOOST_CHECK(result == true); BOOST_CHECK(result == true);
fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path walletdir = m_args.GetPathArg("-walletdir");
fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]);
BOOST_CHECK_EQUAL(walletdir, expected_path); BOOST_CHECK_EQUAL(walletdir, expected_path);
} }

View File

@ -221,7 +221,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash())); WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(newTip->nHeight, newTip->GetBlockHash()));
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
AddWallet(context, wallet); AddWallet(context, wallet);
UniValue keys; UniValue keys;
keys.setArray(); keys.setArray();
@ -277,12 +277,12 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
SetMockTime(KEY_TIME); SetMockTime(KEY_TIME);
m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]);
std::string backup_file = fs::PathToString(gArgs.GetDataDirNet() / "wallet.backup"); std::string backup_file = fs::PathToString(m_args.GetDataDirNet() / "wallet.backup");
// Import key into wallet and call dumpwallet to create backup file. // Import key into wallet and call dumpwallet to create backup file.
{ {
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase()); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(m_node.chain.get(), "", m_args, CreateDummyWalletDatabase());
{ {
auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan();
@ -310,7 +310,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
JSONRPCRequest request; JSONRPCRequest request;
request.context = &context; request.context = &context;
request.params.setArray(); request.params.setArray();
@ -716,10 +716,10 @@ BOOST_FIXTURE_TEST_CASE(wallet_descriptor_test, BasicTestingSetup)
//! rescanning where new transactions in new blocks could be lost. //! rescanning where new transactions in new blocks could be lost.
BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
{ {
gArgs.ForceSetArg("-unsafesqlitesync", "1"); m_args.ForceSetArg("-unsafesqlitesync", "1");
// Create new wallet with known key and unload it. // Create new wallet with known key and unload it.
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
context.chain = m_node.chain.get(); context.chain = m_node.chain.get();
auto wallet = TestLoadWallet(context); auto wallet = TestLoadWallet(context);
CKey key; CKey key;
@ -812,7 +812,7 @@ BOOST_FIXTURE_TEST_CASE(CreateWallet, TestChain100Setup)
BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup) BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup)
{ {
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
auto wallet = TestLoadWallet(context); auto wallet = TestLoadWallet(context);
BOOST_CHECK(wallet); BOOST_CHECK(wallet);
UnloadWallet(std::move(wallet)); UnloadWallet(std::move(wallet));
@ -820,9 +820,9 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletWithoutChain, BasicTestingSetup)
BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup)
{ {
gArgs.ForceSetArg("-unsafesqlitesync", "1"); m_args.ForceSetArg("-unsafesqlitesync", "1");
WalletContext context; WalletContext context;
context.args = &gArgs; context.args = &m_args;
context.chain = m_node.chain.get(); context.chain = m_node.chain.get();
auto wallet = TestLoadWallet(context); auto wallet = TestLoadWallet(context);
CKey key; CKey key;

View File

@ -366,6 +366,7 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string&
std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{ {
DatabaseOptions options; DatabaseOptions options;
ReadDatabaseArgs(*context.args, options);
options.require_existing = true; options.require_existing = true;
if (!fs::exists(backup_file)) { if (!fs::exists(backup_file)) {

View File

@ -1189,10 +1189,11 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase()
/** Return object for accessing temporary in-memory database. */ /** Return object for accessing temporary in-memory database. */
std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() std::unique_ptr<WalletDatabase> CreateMockWalletDatabase()
{ {
DatabaseOptions options;
#ifdef USE_SQLITE #ifdef USE_SQLITE
return std::make_unique<SQLiteDatabase>("", "", true); return std::make_unique<SQLiteDatabase>("", "", options, true);
#elif USE_BDB #elif USE_BDB
return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "", options);
#endif #endif
} }
} // namespace wallet } // namespace wallet

View File

@ -140,6 +140,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
if (command == "create") { if (command == "create") {
DatabaseOptions options; DatabaseOptions options;
ReadDatabaseArgs(args, options);
options.require_create = true; options.require_create = true;
// If -legacy is set, use it. Otherwise default to false. // If -legacy is set, use it. Otherwise default to false.
bool make_legacy = args.GetBoolArg("-legacy", false); bool make_legacy = args.GetBoolArg("-legacy", false);
@ -165,6 +166,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} }
} else if (command == "info") { } else if (command == "info") {
DatabaseOptions options; DatabaseOptions options;
ReadDatabaseArgs(args, options);
options.require_existing = true; options.require_existing = true;
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options); const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false; if (!wallet_instance) return false;
@ -174,7 +176,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
#ifdef USE_BDB #ifdef USE_BDB
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
bool ret = RecoverDatabaseFile(path, error, warnings); bool ret = RecoverDatabaseFile(args, path, error, warnings);
if (!ret) { if (!ret) {
for (const auto& warning : warnings) { for (const auto& warning : warnings) {
tfm::format(std::cerr, "%s\n", warning.original); tfm::format(std::cerr, "%s\n", warning.original);
@ -190,11 +192,12 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
#endif #endif
} else if (command == "dump") { } else if (command == "dump") {
DatabaseOptions options; DatabaseOptions options;
ReadDatabaseArgs(args, options);
options.require_existing = true; options.require_existing = true;
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options); const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false; if (!wallet_instance) return false;
bilingual_str error; bilingual_str error;
bool ret = DumpWallet(*wallet_instance, error); bool ret = DumpWallet(args, *wallet_instance, error);
if (!ret && !error.empty()) { if (!ret && !error.empty()) {
tfm::format(std::cerr, "%s\n", error.original); tfm::format(std::cerr, "%s\n", error.original);
return ret; return ret;
@ -204,7 +207,7 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
} else if (command == "createfromdump") { } else if (command == "createfromdump") {
bilingual_str error; bilingual_str error;
std::vector<bilingual_str> warnings; std::vector<bilingual_str> warnings;
bool ret = CreateFromDump(name, path, error, warnings); bool ret = CreateFromDump(args, name, path, error, warnings);
for (const auto& warning : warnings) { for (const auto& warning : warnings) {
tfm::format(std::cout, "%s\n", warning.original); tfm::format(std::cout, "%s\n", warning.original);
} }