Skip to content

Commit

Permalink
wallet: Add MakeDatabase function
Browse files Browse the repository at this point in the history
Summary:
New function is not currently called but will be called in upcoming commits. It
moves database path checking, and existence checking, and already-loaded
checking, and verification into a single function so this logic does not need
to be repeated all over higher level wallet code, and so higher level code does
not need to change when SQLite support is added in
bitcoin/bitcoin#19077. This also lets higher level
wallet code make fewer assumptions about the contents of wallet directories.

This commit just adds the new function and does not change behavior in any way.

This is a backport of [[bitcoin/bitcoin#19619 | core#19619]] [2/8]
bitcoin/bitcoin@b5b4141

Depends on D10224

Test Plan: `ninja`

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Differential Revision: https://reviews.bitcoinabc.org/D10225
  • Loading branch information
ryanofsky authored and PiRK committed Aug 16, 2022
1 parent 377228a commit 7a59705
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 1 deletion.
37 changes: 37 additions & 0 deletions src/wallet/bdb.cpp
Expand Up @@ -909,3 +909,40 @@ std::unique_ptr<DatabaseBatch>
BerkeleyDatabase::MakeBatch(const char *mode, bool flush_on_close) {
return std::make_unique<BerkeleyBatch>(*this, mode, flush_on_close);
}

bool ExistsBerkeleyDatabase(const fs::path &path) {
fs::path env_directory;
std::string data_filename;
SplitWalletPath(path, env_directory, data_filename);
return IsBerkeleyBtree(env_directory / data_filename);
}

std::unique_ptr<BerkeleyDatabase>
MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options,
DatabaseStatus &status, bilingual_str &error) {
std::unique_ptr<BerkeleyDatabase> db;
{
// Lock env.m_databases until insert in BerkeleyDatabase constructor
LOCK(cs_db);
std::string data_filename;
std::shared_ptr<BerkeleyEnvironment> env =
GetWalletEnv(path, data_filename);
if (env->m_databases.count(data_filename)) {
error = Untranslated(strprintf(
"Refusing to load database. Data file '%s' is already loaded.",
(env->Directory() / data_filename).string()));
status = DatabaseStatus::FAILED_ALREADY_LOADED;
return nullptr;
}
db = std::make_unique<BerkeleyDatabase>(std::move(env),
std::move(data_filename));
}

if (options.verify && !db->Verify(error)) {
status = DatabaseStatus::FAILED_VERIFY;
return nullptr;
}

status = DatabaseStatus::SUCCESS;
return db;
}
11 changes: 11 additions & 0 deletions src/wallet/bdb.h
Expand Up @@ -87,6 +87,9 @@ GetWalletEnv(const fs::path &wallet_path, std::string &database_filename);
/** Return whether a BDB wallet database is currently loaded. */
bool IsBDBWalletLoaded(const fs::path &wallet_path);

/** Check format of database file */
bool IsBerkeleyBtree(const fs::path &path);

class BerkeleyBatch;

/**
Expand Down Expand Up @@ -245,4 +248,12 @@ class BerkeleyBatch : public DatabaseBatch {

std::string BerkeleyDatabaseVersion();

//! Check if Berkeley database exists at specified path.
bool ExistsBerkeleyDatabase(const fs::path &path);

//! Return object giving access to Berkeley database at specified path.
std::unique_ptr<BerkeleyDatabase>
MakeBerkeleyDatabase(const fs::path &path, const DatabaseOptions &options,
DatabaseStatus &status, bilingual_str &error);

#endif // BITCOIN_WALLET_BDB_H
25 changes: 25 additions & 0 deletions src/wallet/db.h
Expand Up @@ -220,4 +220,29 @@ class DummyDatabase : public WalletDatabase {
}
};

enum class DatabaseFormat {
BERKELEY,
};

struct DatabaseOptions {
bool require_existing = false;
bool require_create = false;
bool verify = true;
};

enum class DatabaseStatus {
SUCCESS,
FAILED_BAD_PATH,
FAILED_BAD_FORMAT,
FAILED_ALREADY_LOADED,
FAILED_ALREADY_EXISTS,
FAILED_NOT_FOUND,
FAILED_VERIFY,
};

std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path &path,
const DatabaseOptions &options,
DatabaseStatus &status,
bilingual_str &error);

#endif // BITCOIN_WALLET_DB_H
49 changes: 49 additions & 0 deletions src/wallet/walletdb.cpp
Expand Up @@ -15,6 +15,8 @@
#include <util/bip32.h>
#include <util/system.h>
#include <util/time.h>
#include <util/translation.h>
#include <wallet/bdb.h>
#include <wallet/wallet.h>

#include <atomic>
Expand Down Expand Up @@ -1109,6 +1111,53 @@ bool WalletBatch::TxnAbort() {
return m_batch->TxnAbort();
}

std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path &path,
const DatabaseOptions &options,
DatabaseStatus &status,
bilingual_str &error) {
bool exists;
try {
exists = fs::symlink_status(path).type() != fs::file_not_found;
} catch (const fs::filesystem_error &e) {
error = Untranslated(
strprintf("Failed to access database path '%s': %s", path.string(),
fsbridge::get_filesystem_error_message(e)));
status = DatabaseStatus::FAILED_BAD_PATH;
return nullptr;
}

std::optional<DatabaseFormat> format;
if (exists) {
if (ExistsBerkeleyDatabase(path)) {
format = DatabaseFormat::BERKELEY;
}
} else if (options.require_existing) {
error = Untranslated(
strprintf("Failed to load database path '%s'. Path does not exist.",
path.string()));
status = DatabaseStatus::FAILED_NOT_FOUND;
return nullptr;
}

if (!format && options.require_existing) {
error = Untranslated(strprintf("Failed to load database path '%s'. "
"Data is not in recognized format.",
path.string()));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}

if (format && options.require_create) {
error = Untranslated(strprintf(
"Failed to create database path '%s'. Database already exists.",
path.string()));
status = DatabaseStatus::FAILED_ALREADY_EXISTS;
return nullptr;
}

return MakeBerkeleyDatabase(path, options, status, error);
}

bool IsWalletLoaded(const fs::path &wallet_path) {
return IsBDBWalletLoaded(wallet_path);
}
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/walletutil.cpp
Expand Up @@ -29,7 +29,7 @@ fs::path GetWalletDir() {
return path;
}

static bool IsBerkeleyBtree(const fs::path &path) {
bool IsBerkeleyBtree(const fs::path &path) {
if (!fs::exists(path)) {
return false;
}
Expand Down

0 comments on commit 7a59705

Please sign in to comment.