diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -71,6 +71,8 @@ ~SQLiteDatabase(); + bool Verify(bilingual_str &error); + /** Open the database if it is not already opened */ void Open() override; diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -132,6 +132,50 @@ } } +bool SQLiteDatabase::Verify(bilingual_str &error) { + assert(m_db); + + sqlite3_stmt *stmt{nullptr}; + int ret = + sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr); + if (ret != SQLITE_OK) { + sqlite3_finalize(stmt); + error = strprintf(_("SQLiteDatabase: Failed to prepare statement to " + "verify database: %s"), + sqlite3_errstr(ret)); + return false; + } + while (true) { + ret = sqlite3_step(stmt); + if (ret == SQLITE_DONE) { + break; + } + if (ret != SQLITE_ROW) { + error = strprintf(_("SQLiteDatabase: Failed to execute statement " + "to verify database: %s"), + sqlite3_errstr(ret)); + break; + } + const char *msg = (const char *)sqlite3_column_text(stmt, 0); + if (!msg) { + error = strprintf(_("SQLiteDatabase: Failed to read database " + "verification error: %s"), + sqlite3_errstr(ret)); + break; + } + std::string str_msg(msg); + if (str_msg == "ok") { + continue; + } + if (error.empty()) { + error = _("Failed to verify database") + Untranslated("\n"); + } + error += Untranslated(strprintf("%s\n", str_msg)); + } + sqlite3_finalize(stmt); + return error.empty(); +} + void SQLiteDatabase::Open() { int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; @@ -564,7 +608,19 @@ std::unique_ptr MakeSQLiteDatabase(const fs::path &path, const DatabaseOptions &options, DatabaseStatus &status, bilingual_str &error) { - return std::make_unique(path, path / DATABASE_FILENAME); + const fs::path file = path / DATABASE_FILENAME; + try { + auto db = std::make_unique(path, file); + if (options.verify && !db->Verify(error)) { + status = DatabaseStatus::FAILED_VERIFY; + return nullptr; + } + return db; + } catch (const std::runtime_error &e) { + status = DatabaseStatus::FAILED_LOAD; + error.original = e.what(); + return nullptr; + } } std::string SQLiteDatabaseVersion() {