Changeset View
Changeset View
Standalone View
Standalone View
src/wallet/sqlite.cpp
// Copyright (c) 2020 The Bitcoin Core developers | // Copyright (c) 2020 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <wallet/sqlite.h> | #include <wallet/sqlite.h> | ||||
#include <chainparams.h> | |||||
#include <crypto/common.h> | |||||
#include <logging.h> | #include <logging.h> | ||||
#include <sync.h> | #include <sync.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <util/translation.h> | #include <util/translation.h> | ||||
#include <wallet/db.h> | #include <wallet/db.h> | ||||
#include <cstdint> | #include <cstdint> | ||||
▲ Show 20 Lines • Show All 115 Lines • ▼ Show 20 Lines | if (--g_sqlite_count == 0) { | ||||
sqlite3_errstr(ret)); | sqlite3_errstr(ret)); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
bool SQLiteDatabase::Verify(bilingual_str &error) { | bool SQLiteDatabase::Verify(bilingual_str &error) { | ||||
assert(m_db); | assert(m_db); | ||||
// Check the application ID matches our network magic | |||||
sqlite3_stmt *app_id_stmt{nullptr}; | |||||
int ret = sqlite3_prepare_v2(m_db, "PRAGMA application_id", -1, | |||||
&app_id_stmt, nullptr); | |||||
if (ret != SQLITE_OK) { | |||||
sqlite3_finalize(app_id_stmt); | |||||
error = strprintf(_("SQLiteDatabase: Failed to prepare the statement " | |||||
"to fetch the application id: %s"), | |||||
sqlite3_errstr(ret)); | |||||
return false; | |||||
} | |||||
ret = sqlite3_step(app_id_stmt); | |||||
if (ret != SQLITE_ROW) { | |||||
sqlite3_finalize(app_id_stmt); | |||||
error = strprintf( | |||||
_("SQLiteDatabase: Failed to fetch the application id: %s"), | |||||
sqlite3_errstr(ret)); | |||||
return false; | |||||
} | |||||
uint32_t app_id = static_cast<uint32_t>(sqlite3_column_int(app_id_stmt, 0)); | |||||
sqlite3_finalize(app_id_stmt); | |||||
uint32_t net_magic = ReadBE32(Params().DiskMagic().data()); | |||||
if (app_id != net_magic) { | |||||
error = strprintf( | |||||
_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), | |||||
net_magic, app_id); | |||||
return false; | |||||
} | |||||
sqlite3_stmt *stmt{nullptr}; | sqlite3_stmt *stmt{nullptr}; | ||||
int ret = | ret = | ||||
sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr); | sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr); | ||||
if (ret != SQLITE_OK) { | if (ret != SQLITE_OK) { | ||||
sqlite3_finalize(stmt); | sqlite3_finalize(stmt); | ||||
error = strprintf(_("SQLiteDatabase: Failed to prepare statement to " | error = strprintf(_("SQLiteDatabase: Failed to prepare statement to " | ||||
"verify database: %s"), | "verify database: %s"), | ||||
sqlite3_errstr(ret)); | sqlite3_errstr(ret)); | ||||
return false; | return false; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | if (!table_exists) { | ||||
"CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, " | "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, " | ||||
"value BLOB NOT NULL)", | "value BLOB NOT NULL)", | ||||
nullptr, nullptr, nullptr); | nullptr, nullptr, nullptr); | ||||
if (ret != SQLITE_OK) { | if (ret != SQLITE_OK) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
strprintf("SQLiteDatabase: Failed to create new database: %s\n", | strprintf("SQLiteDatabase: Failed to create new database: %s\n", | ||||
sqlite3_errstr(ret))); | sqlite3_errstr(ret))); | ||||
} | } | ||||
// Set the application id | |||||
uint32_t app_id = ReadBE32(Params().DiskMagic().data()); | |||||
std::string set_app_id = strprintf("PRAGMA application_id = %d", | |||||
static_cast<int32_t>(app_id)); | |||||
ret = sqlite3_exec(m_db, set_app_id.c_str(), nullptr, nullptr, nullptr); | |||||
if (ret != SQLITE_OK) { | |||||
throw std::runtime_error(strprintf( | |||||
"SQLiteDatabase: Failed to set the application id: %s\n", | |||||
sqlite3_errstr(ret))); | |||||
} | |||||
} | } | ||||
} | } | ||||
bool SQLiteDatabase::Rewrite(const char *skip) { | bool SQLiteDatabase::Rewrite(const char *skip) { | ||||
// Rewrite the database using the VACUUM command: | // Rewrite the database using the VACUUM command: | ||||
// https://sqlite.org/lang_vacuum.html | // https://sqlite.org/lang_vacuum.html | ||||
int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr); | int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr); | ||||
return ret == SQLITE_OK; | return ret == SQLITE_OK; | ||||
▲ Show 20 Lines • Show All 359 Lines • ▼ Show 20 Lines | bool IsSQLiteFile(const fs::path &path) { | ||||
fsbridge::ifstream file(path, std::ios::binary); | fsbridge::ifstream file(path, std::ios::binary); | ||||
if (!file.is_open()) { | if (!file.is_open()) { | ||||
return false; | return false; | ||||
} | } | ||||
// Magic is at beginning and is 16 bytes long | // Magic is at beginning and is 16 bytes long | ||||
char magic[16]; | char magic[16]; | ||||
file.read(magic, 16); | file.read(magic, 16); | ||||
// Application id is at offset 68 and 4 bytes long | |||||
file.seekg(68, std::ios::beg); | |||||
char app_id[4]; | |||||
file.read(app_id, 4); | |||||
file.close(); | file.close(); | ||||
// Check the magic, see https://sqlite.org/fileformat2.html | // Check the magic, see https://sqlite.org/fileformat2.html | ||||
std::string magic_str(magic, 16); | std::string magic_str(magic, 16); | ||||
return magic_str == std::string("SQLite format 3", 16); | if (magic_str != std::string("SQLite format 3", 16)) { | ||||
return false; | |||||
} | |||||
// Check the application id matches our network magic | |||||
return memcmp(Params().DiskMagic().data(), app_id, 4) == 0; | |||||
} | } |