/Users/eugenesiegel/btc/bitcoin/src/wallet/sqlite.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | // Copyright (c) 2020-present The Bitcoin Core developers | 
| 2 |  | // Distributed under the MIT software license, see the accompanying | 
| 3 |  | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | 
| 4 |  |  | 
| 5 |  | #include <bitcoin-build-config.h> // IWYU pragma: keep | 
| 6 |  |  | 
| 7 |  | #include <wallet/sqlite.h> | 
| 8 |  |  | 
| 9 |  | #include <chainparams.h> | 
| 10 |  | #include <crypto/common.h> | 
| 11 |  | #include <logging.h> | 
| 12 |  | #include <sync.h> | 
| 13 |  | #include <util/check.h> | 
| 14 |  | #include <util/fs_helpers.h> | 
| 15 |  | #include <util/strencodings.h> | 
| 16 |  | #include <util/translation.h> | 
| 17 |  | #include <wallet/db.h> | 
| 18 |  |  | 
| 19 |  | #include <sqlite3.h> | 
| 20 |  |  | 
| 21 |  | #include <cstdint> | 
| 22 |  | #include <optional> | 
| 23 |  | #include <utility> | 
| 24 |  | #include <vector> | 
| 25 |  |  | 
| 26 |  | namespace wallet { | 
| 27 |  | static constexpr int32_t WALLET_SCHEMA_VERSION = 0; | 
| 28 |  |  | 
| 29 |  | static std::span<const std::byte> SpanFromBlob(sqlite3_stmt* stmt, int col) | 
| 30 | 0 | { | 
| 31 | 0 |     return {reinterpret_cast<const std::byte*>(sqlite3_column_blob(stmt, col)), | 
| 32 | 0 |             static_cast<size_t>(sqlite3_column_bytes(stmt, col))}; | 
| 33 | 0 | } | 
| 34 |  |  | 
| 35 |  | static void ErrorLogCallback(void* arg, int code, const char* msg) | 
| 36 | 0 | { | 
| 37 |  |     // From sqlite3_config() documentation for the SQLITE_CONFIG_LOG option: | 
| 38 |  |     // "The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as | 
| 39 |  |     // the first parameter to the application-defined logger function whenever that function is | 
| 40 |  |     // invoked." | 
| 41 |  |     // Assert that this is the case: | 
| 42 | 0 |     assert(arg == nullptr); | 
| 43 | 0 |     LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg);| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 44 | 0 | } | 
| 45 |  |  | 
| 46 |  | static int TraceSqlCallback(unsigned code, void* context, void* param1, void* param2) | 
| 47 | 0 | { | 
| 48 | 0 |     auto* db = static_cast<SQLiteDatabase*>(context); | 
| 49 | 0 |     if (code == SQLITE_TRACE_STMT) { | 
| 50 | 0 |         auto* stmt = static_cast<sqlite3_stmt*>(param1); | 
| 51 |  |         // To be conservative and avoid leaking potentially secret information | 
| 52 |  |         // in the log file, only expand statements that query the database, not | 
| 53 |  |         // statements that update the database. | 
| 54 | 0 |         char* expanded{sqlite3_stmt_readonly(stmt) ? sqlite3_expanded_sql(stmt) : nullptr}; | 
| 55 | 0 |         LogTrace(BCLog::WALLETDB, "[%s] SQLite Statement: %s\n", db->Filename(), expanded ? expanded : sqlite3_sql(stmt)); | Line | Count | Source |  | 382 | 0 | #define LogTrace(category, ...) LogPrintLevel(category, BCLog::Level::Trace, __VA_ARGS__) | Line | Count | Source |  | 373 | 0 |     do {                                                              \ |  | 374 | 0 |         if (LogAcceptCategory((category), (level))) {                 \ |  | 375 | 0 |             bool rate_limit{level >= BCLog::Level::Info};             \ |  | 376 | 0 |             LogPrintLevel_(category, level, rate_limit, __VA_ARGS__); \ | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 |  | 377 | 0 |         }                                                             \ |  | 378 | 0 |     } while (0) | 
 | 
 | 
| 56 | 0 |         if (expanded) sqlite3_free(expanded); | 
| 57 | 0 |     } | 
| 58 | 0 |     return SQLITE_OK; | 
| 59 | 0 | } | 
| 60 |  |  | 
| 61 |  | static bool BindBlobToStatement(sqlite3_stmt* stmt, | 
| 62 |  |                                 int index, | 
| 63 |  |                                 std::span<const std::byte> blob, | 
| 64 |  |                                 const std::string& description) | 
| 65 | 0 | { | 
| 66 |  |     // Pass a pointer to the empty string "" below instead of passing the | 
| 67 |  |     // blob.data() pointer if the blob.data() pointer is null. Passing a null | 
| 68 |  |     // data pointer to bind_blob would cause sqlite to bind the SQL NULL value | 
| 69 |  |     // instead of the empty blob value X'', which would mess up SQL comparisons. | 
| 70 | 0 |     int res = sqlite3_bind_blob(stmt, index, blob.data() ? static_cast<const void*>(blob.data()) : "", blob.size(), SQLITE_STATIC); | 
| 71 | 0 |     if (res != SQLITE_OK) { | 
| 72 | 0 |         LogPrintf("Unable to bind %s to statement: %s\n", description, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 73 | 0 |         sqlite3_clear_bindings(stmt); | 
| 74 | 0 |         sqlite3_reset(stmt); | 
| 75 | 0 |         return false; | 
| 76 | 0 |     } | 
| 77 |  |  | 
| 78 | 0 |     return true; | 
| 79 | 0 | } | 
| 80 |  |  | 
| 81 |  | static std::optional<int> ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error) | 
| 82 | 0 | { | 
| 83 | 0 |     std::string stmt_text = strprintf("PRAGMA %s", key);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 84 | 0 |     sqlite3_stmt* pragma_read_stmt{nullptr}; | 
| 85 | 0 |     int ret = sqlite3_prepare_v2(db, stmt_text.c_str(), -1, &pragma_read_stmt, nullptr); | 
| 86 | 0 |     if (ret != SQLITE_OK) { | 
| 87 | 0 |         sqlite3_finalize(pragma_read_stmt); | 
| 88 | 0 |         error = Untranslated(strprintf("SQLiteDatabase: Failed to prepare the statement to fetch %s: %s", description, sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 89 | 0 |         return std::nullopt; | 
| 90 | 0 |     } | 
| 91 | 0 |     ret = sqlite3_step(pragma_read_stmt); | 
| 92 | 0 |     if (ret != SQLITE_ROW) { | 
| 93 | 0 |         sqlite3_finalize(pragma_read_stmt); | 
| 94 | 0 |         error = Untranslated(strprintf("SQLiteDatabase: Failed to fetch %s: %s", description, sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 95 | 0 |         return std::nullopt; | 
| 96 | 0 |     } | 
| 97 | 0 |     int result = sqlite3_column_int(pragma_read_stmt, 0); | 
| 98 | 0 |     sqlite3_finalize(pragma_read_stmt); | 
| 99 | 0 |     return result; | 
| 100 | 0 | } | 
| 101 |  |  | 
| 102 |  | static void SetPragma(sqlite3* db, const std::string& key, const std::string& value, const std::string& err_msg) | 
| 103 | 0 | { | 
| 104 | 0 |     std::string stmt_text = strprintf("PRAGMA %s = %s", key, value);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 105 | 0 |     int ret = sqlite3_exec(db, stmt_text.c_str(), nullptr, nullptr, nullptr); | 
| 106 | 0 |     if (ret != SQLITE_OK) { | 
| 107 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: %s: %s\n", err_msg, sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 108 | 0 |     } | 
| 109 | 0 | } | 
| 110 |  |  | 
| 111 |  | Mutex SQLiteDatabase::g_sqlite_mutex; | 
| 112 |  | int SQLiteDatabase::g_sqlite_count = 0; | 
| 113 |  |  | 
| 114 |  | SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock) | 
| 115 | 0 |     : WalletDatabase(), m_mock(mock), m_dir_path(dir_path), m_file_path(fs::PathToString(file_path)), m_write_semaphore(1), m_use_unsafe_sync(options.use_unsafe_sync) | 
| 116 | 0 | { | 
| 117 | 0 |     { | 
| 118 | 0 |         LOCK(g_sqlite_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 119 | 0 |         if (++g_sqlite_count == 1) { | 
| 120 |  |             // Setup logging | 
| 121 | 0 |             int ret = sqlite3_config(SQLITE_CONFIG_LOG, ErrorLogCallback, nullptr); | 
| 122 | 0 |             if (ret != SQLITE_OK) { | 
| 123 | 0 |                 throw std::runtime_error(strprintf("SQLiteDatabase: Failed to setup error log: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 124 | 0 |             } | 
| 125 |  |             // Force serialized threading mode | 
| 126 | 0 |             ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED); | 
| 127 | 0 |             if (ret != SQLITE_OK) { | 
| 128 | 0 |                 throw std::runtime_error(strprintf("SQLiteDatabase: Failed to configure serialized threading mode: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 129 | 0 |             } | 
| 130 | 0 |         } | 
| 131 | 0 |         int ret = sqlite3_initialize(); // This is a no-op if sqlite3 is already initialized | 
| 132 | 0 |         if (ret != SQLITE_OK) { | 
| 133 | 0 |             throw std::runtime_error(strprintf("SQLiteDatabase: Failed to initialize SQLite: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 134 | 0 |         } | 
| 135 | 0 |     } | 
| 136 |  |  | 
| 137 | 0 |     try { | 
| 138 | 0 |         Open(); | 
| 139 | 0 |     } catch (const std::runtime_error&) { | 
| 140 |  |         // If open fails, cleanup this object and rethrow the exception | 
| 141 | 0 |         Cleanup(); | 
| 142 | 0 |         throw; | 
| 143 | 0 |     } | 
| 144 | 0 | } | 
| 145 |  |  | 
| 146 |  | void SQLiteBatch::SetupSQLStatements() | 
| 147 | 0 | { | 
| 148 | 0 |     const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{ | 
| 149 | 0 |         {&m_read_stmt, "SELECT value FROM main WHERE key = ?"}, | 
| 150 | 0 |         {&m_insert_stmt, "INSERT INTO main VALUES(?, ?)"}, | 
| 151 | 0 |         {&m_overwrite_stmt, "INSERT or REPLACE into main values(?, ?)"}, | 
| 152 | 0 |         {&m_delete_stmt, "DELETE FROM main WHERE key = ?"}, | 
| 153 | 0 |         {&m_delete_prefix_stmt, "DELETE FROM main WHERE instr(key, ?) = 1"}, | 
| 154 | 0 |     }; | 
| 155 |  | 
 | 
| 156 | 0 |     for (const auto& [stmt_prepared, stmt_text] : statements) { | 
| 157 | 0 |         if (*stmt_prepared == nullptr) { | 
| 158 | 0 |             int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, stmt_prepared, nullptr); | 
| 159 | 0 |             if (res != SQLITE_OK) { | 
| 160 | 0 |                 throw std::runtime_error(strprintf( | Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 161 | 0 |                     "SQLiteDatabase: Failed to setup SQL statements: %s\n", sqlite3_errstr(res))); | 
| 162 | 0 |             } | 
| 163 | 0 |         } | 
| 164 | 0 |     } | 
| 165 | 0 | } | 
| 166 |  |  | 
| 167 |  | SQLiteDatabase::~SQLiteDatabase() | 
| 168 | 0 | { | 
| 169 | 0 |     Cleanup(); | 
| 170 | 0 | } | 
| 171 |  |  | 
| 172 |  | void SQLiteDatabase::Cleanup() noexcept | 
| 173 | 0 | { | 
| 174 | 0 |     AssertLockNotHeld(g_sqlite_mutex); | Line | Count | Source |  | 142 | 0 | #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs) | 
 | 
| 175 |  | 
 | 
| 176 | 0 |     Close(); | 
| 177 |  | 
 | 
| 178 | 0 |     LOCK(g_sqlite_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 179 | 0 |     if (--g_sqlite_count == 0) { | 
| 180 | 0 |         int ret = sqlite3_shutdown(); | 
| 181 | 0 |         if (ret != SQLITE_OK) { | 
| 182 | 0 |             LogPrintf("SQLiteDatabase: Failed to shutdown SQLite: %s\n", sqlite3_errstr(ret));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 183 | 0 |         } | 
| 184 | 0 |     } | 
| 185 | 0 | } | 
| 186 |  |  | 
| 187 |  | bool SQLiteDatabase::Verify(bilingual_str& error) | 
| 188 | 0 | { | 
| 189 | 0 |     assert(m_db); | 
| 190 |  |  | 
| 191 |  |     // Check the application ID matches our network magic | 
| 192 | 0 |     auto read_result = ReadPragmaInteger(m_db, "application_id", "the application id", error); | 
| 193 | 0 |     if (!read_result.has_value()) return false; | 
| 194 | 0 |     uint32_t app_id = static_cast<uint32_t>(read_result.value()); | 
| 195 | 0 |     uint32_t net_magic = ReadBE32(Params().MessageStart().data()); | 
| 196 | 0 |     if (app_id != net_magic) { | 
| 197 | 0 |         error = strprintf(_("SQLiteDatabase: Unexpected application id. Expected %u, got %u"), net_magic, app_id);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 198 | 0 |         return false; | 
| 199 | 0 |     } | 
| 200 |  |  | 
| 201 |  |     // Check our schema version | 
| 202 | 0 |     read_result = ReadPragmaInteger(m_db, "user_version", "sqlite wallet schema version", error); | 
| 203 | 0 |     if (!read_result.has_value()) return false; | 
| 204 | 0 |     int32_t user_ver = read_result.value(); | 
| 205 | 0 |     if (user_ver != WALLET_SCHEMA_VERSION) { | 
| 206 | 0 |         error = strprintf(_("SQLiteDatabase: Unknown sqlite wallet schema version %d. Only version %d is supported"), user_ver, WALLET_SCHEMA_VERSION);| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 207 | 0 |         return false; | 
| 208 | 0 |     } | 
| 209 |  |  | 
| 210 | 0 |     sqlite3_stmt* stmt{nullptr}; | 
| 211 | 0 |     int ret = sqlite3_prepare_v2(m_db, "PRAGMA integrity_check", -1, &stmt, nullptr); | 
| 212 | 0 |     if (ret != SQLITE_OK) { | 
| 213 | 0 |         sqlite3_finalize(stmt); | 
| 214 | 0 |         error = strprintf(_("SQLiteDatabase: Failed to prepare statement to verify database: %s"), sqlite3_errstr(ret));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 215 | 0 |         return false; | 
| 216 | 0 |     } | 
| 217 | 0 |     while (true) { | 
| 218 | 0 |         ret = sqlite3_step(stmt); | 
| 219 | 0 |         if (ret == SQLITE_DONE) { | 
| 220 | 0 |             break; | 
| 221 | 0 |         } | 
| 222 | 0 |         if (ret != SQLITE_ROW) { | 
| 223 | 0 |             error = strprintf(_("SQLiteDatabase: Failed to execute statement to verify database: %s"), sqlite3_errstr(ret));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 224 | 0 |             break; | 
| 225 | 0 |         } | 
| 226 | 0 |         const char* msg = (const char*)sqlite3_column_text(stmt, 0); | 
| 227 | 0 |         if (!msg) { | 
| 228 | 0 |             error = strprintf(_("SQLiteDatabase: Failed to read database verification error: %s"), sqlite3_errstr(ret));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 229 | 0 |             break; | 
| 230 | 0 |         } | 
| 231 | 0 |         std::string str_msg(msg); | 
| 232 | 0 |         if (str_msg == "ok") { | 
| 233 | 0 |             continue; | 
| 234 | 0 |         } | 
| 235 | 0 |         if (error.empty()) { | 
| 236 | 0 |             error = _("Failed to verify database") + Untranslated("\n"); | 
| 237 | 0 |         } | 
| 238 | 0 |         error += Untranslated(strprintf("%s\n", str_msg));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 239 | 0 |     } | 
| 240 | 0 |     sqlite3_finalize(stmt); | 
| 241 | 0 |     return error.empty(); | 
| 242 | 0 | } | 
| 243 |  |  | 
| 244 |  | void SQLiteDatabase::Open() | 
| 245 | 0 | { | 
| 246 | 0 |     int flags = SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; | 
| 247 | 0 |     if (m_mock) { | 
| 248 | 0 |         flags |= SQLITE_OPEN_MEMORY; // In memory database for mock db | 
| 249 | 0 |     } | 
| 250 |  | 
 | 
| 251 | 0 |     if (m_db == nullptr) { | 
| 252 | 0 |         if (!m_mock) { | 
| 253 | 0 |             TryCreateDirectories(m_dir_path); | 
| 254 | 0 |         } | 
| 255 | 0 |         int ret = sqlite3_open_v2(m_file_path.c_str(), &m_db, flags, nullptr); | 
| 256 | 0 |         if (ret != SQLITE_OK) { | 
| 257 | 0 |             throw std::runtime_error(strprintf("SQLiteDatabase: Failed to open database: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 258 | 0 |         } | 
| 259 | 0 |         ret = sqlite3_extended_result_codes(m_db, 1); | 
| 260 | 0 |         if (ret != SQLITE_OK) { | 
| 261 | 0 |             throw std::runtime_error(strprintf("SQLiteDatabase: Failed to enable extended result codes: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 262 | 0 |         } | 
| 263 |  |         // Trace SQL statements if tracing is enabled with -debug=walletdb -loglevel=walletdb:trace | 
| 264 | 0 |         if (LogAcceptCategory(BCLog::WALLETDB, BCLog::Level::Trace)) { | 
| 265 | 0 |            ret = sqlite3_trace_v2(m_db, SQLITE_TRACE_STMT, TraceSqlCallback, this); | 
| 266 | 0 |            if (ret != SQLITE_OK) { | 
| 267 | 0 |                LogPrintf("Failed to enable SQL tracing for %s\n", Filename());| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 268 | 0 |            } | 
| 269 | 0 |         } | 
| 270 | 0 |     } | 
| 271 |  |  | 
| 272 | 0 |     if (sqlite3_db_readonly(m_db, "main") != 0) { | 
| 273 | 0 |         throw std::runtime_error("SQLiteDatabase: Database opened in readonly mode but read-write permissions are needed"); | 
| 274 | 0 |     } | 
| 275 |  |  | 
| 276 |  |     // Acquire an exclusive lock on the database | 
| 277 |  |     // First change the locking mode to exclusive | 
| 278 | 0 |     SetPragma(m_db, "locking_mode", "exclusive", "Unable to change database locking mode to exclusive"); | 
| 279 |  |     // Now begin a transaction to acquire the exclusive lock. This lock won't be released until we close because of the exclusive locking mode. | 
| 280 | 0 |     int ret = sqlite3_exec(m_db, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr, nullptr); | 
| 281 | 0 |     if (ret != SQLITE_OK) { | 
| 282 | 0 |         throw std::runtime_error("SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of " CLIENT_NAME "?\n"); | 
| 283 | 0 |     } | 
| 284 | 0 |     ret = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr); | 
| 285 | 0 |     if (ret != SQLITE_OK) { | 
| 286 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: Unable to end exclusive lock transaction: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 287 | 0 |     } | 
| 288 |  |  | 
| 289 |  |     // Enable fullfsync for the platforms that use it | 
| 290 | 0 |     SetPragma(m_db, "fullfsync", "true", "Failed to enable fullfsync"); | 
| 291 |  | 
 | 
| 292 | 0 |     if (m_use_unsafe_sync) { | 
| 293 |  |         // Use normal synchronous mode for the journal | 
| 294 | 0 |         LogPrintf("WARNING SQLite is configured to not wait for data to be flushed to disk. Data loss and corruption may occur.\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 295 | 0 |         SetPragma(m_db, "synchronous", "OFF", "Failed to set synchronous mode to OFF"); | 
| 296 | 0 |     } | 
| 297 |  |  | 
| 298 |  |     // Make the table for our key-value pairs | 
| 299 |  |     // First check that the main table exists | 
| 300 | 0 |     sqlite3_stmt* check_main_stmt{nullptr}; | 
| 301 | 0 |     ret = sqlite3_prepare_v2(m_db, "SELECT name FROM sqlite_master WHERE type='table' AND name='main'", -1, &check_main_stmt, nullptr); | 
| 302 | 0 |     if (ret != SQLITE_OK) { | 
| 303 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: Failed to prepare statement to check table existence: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 304 | 0 |     } | 
| 305 | 0 |     ret = sqlite3_step(check_main_stmt); | 
| 306 | 0 |     if (sqlite3_finalize(check_main_stmt) != SQLITE_OK) { | 
| 307 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: Failed to finalize statement checking table existence: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 308 | 0 |     } | 
| 309 | 0 |     bool table_exists; | 
| 310 | 0 |     if (ret == SQLITE_DONE) { | 
| 311 | 0 |         table_exists = false; | 
| 312 | 0 |     } else if (ret == SQLITE_ROW) { | 
| 313 | 0 |         table_exists = true; | 
| 314 | 0 |     } else { | 
| 315 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: Failed to execute statement to check table existence: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 316 | 0 |     } | 
| 317 |  |  | 
| 318 |  |     // Do the db setup things because the table doesn't exist only when we are creating a new wallet | 
| 319 | 0 |     if (!table_exists) { | 
| 320 | 0 |         ret = sqlite3_exec(m_db, "CREATE TABLE main(key BLOB PRIMARY KEY NOT NULL, value BLOB NOT NULL)", nullptr, nullptr, nullptr); | 
| 321 | 0 |         if (ret != SQLITE_OK) { | 
| 322 | 0 |             throw std::runtime_error(strprintf("SQLiteDatabase: Failed to create new database: %s\n", sqlite3_errstr(ret)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 323 | 0 |         } | 
| 324 |  |  | 
| 325 |  |         // Set the application id | 
| 326 | 0 |         uint32_t app_id = ReadBE32(Params().MessageStart().data()); | 
| 327 | 0 |         SetPragma(m_db, "application_id", strprintf("%d", static_cast<int32_t>(app_id)),| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 328 | 0 |                   "Failed to set the application id"); | 
| 329 |  |  | 
| 330 |  |         // Set the user version | 
| 331 | 0 |         SetPragma(m_db, "user_version", strprintf("%d", WALLET_SCHEMA_VERSION),| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 332 | 0 |                   "Failed to set the wallet schema version"); | 
| 333 | 0 |     } | 
| 334 | 0 | } | 
| 335 |  |  | 
| 336 |  | bool SQLiteDatabase::Rewrite() | 
| 337 | 0 | { | 
| 338 |  |     // Rewrite the database using the VACUUM command: https://sqlite.org/lang_vacuum.html | 
| 339 | 0 |     int ret = sqlite3_exec(m_db, "VACUUM", nullptr, nullptr, nullptr); | 
| 340 | 0 |     return ret == SQLITE_OK; | 
| 341 | 0 | } | 
| 342 |  |  | 
| 343 |  | bool SQLiteDatabase::Backup(const std::string& dest) const | 
| 344 | 0 | { | 
| 345 | 0 |     sqlite3* db_copy; | 
| 346 | 0 |     int res = sqlite3_open(dest.c_str(), &db_copy); | 
| 347 | 0 |     if (res != SQLITE_OK) { | 
| 348 | 0 |         sqlite3_close(db_copy); | 
| 349 | 0 |         return false; | 
| 350 | 0 |     } | 
| 351 | 0 |     sqlite3_backup* backup = sqlite3_backup_init(db_copy, "main", m_db, "main"); | 
| 352 | 0 |     if (!backup) { | 
| 353 | 0 |         LogPrintf("%s: Unable to begin backup: %s\n", __func__, sqlite3_errmsg(m_db));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 354 | 0 |         sqlite3_close(db_copy); | 
| 355 | 0 |         return false; | 
| 356 | 0 |     } | 
| 357 |  |     // Specifying -1 will copy all of the pages | 
| 358 | 0 |     res = sqlite3_backup_step(backup, -1); | 
| 359 | 0 |     if (res != SQLITE_DONE) { | 
| 360 | 0 |         LogPrintf("%s: Unable to backup: %s\n", __func__, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 361 | 0 |         sqlite3_backup_finish(backup); | 
| 362 | 0 |         sqlite3_close(db_copy); | 
| 363 | 0 |         return false; | 
| 364 | 0 |     } | 
| 365 | 0 |     res = sqlite3_backup_finish(backup); | 
| 366 | 0 |     sqlite3_close(db_copy); | 
| 367 | 0 |     return res == SQLITE_OK; | 
| 368 | 0 | } | 
| 369 |  |  | 
| 370 |  | void SQLiteDatabase::Close() | 
| 371 | 0 | { | 
| 372 | 0 |     int res = sqlite3_close(m_db); | 
| 373 | 0 |     if (res != SQLITE_OK) { | 
| 374 | 0 |         throw std::runtime_error(strprintf("SQLiteDatabase: Failed to close database: %s\n", sqlite3_errstr(res)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 375 | 0 |     } | 
| 376 | 0 |     m_db = nullptr; | 
| 377 | 0 | } | 
| 378 |  |  | 
| 379 |  | bool SQLiteDatabase::HasActiveTxn() | 
| 380 | 0 | { | 
| 381 |  |     // 'sqlite3_get_autocommit' returns true by default, and false if a transaction has begun and not been committed or rolled back. | 
| 382 | 0 |     return m_db && sqlite3_get_autocommit(m_db) == 0; | 
| 383 | 0 | } | 
| 384 |  |  | 
| 385 |  | int SQliteExecHandler::Exec(SQLiteDatabase& database, const std::string& statement) | 
| 386 | 0 | { | 
| 387 | 0 |     return sqlite3_exec(database.m_db, statement.data(), nullptr, nullptr, nullptr); | 
| 388 | 0 | } | 
| 389 |  |  | 
| 390 |  | std::unique_ptr<DatabaseBatch> SQLiteDatabase::MakeBatch() | 
| 391 | 0 | { | 
| 392 |  |     // We ignore flush_on_close because we don't do manual flushing for SQLite | 
| 393 | 0 |     return std::make_unique<SQLiteBatch>(*this); | 
| 394 | 0 | } | 
| 395 |  |  | 
| 396 |  | SQLiteBatch::SQLiteBatch(SQLiteDatabase& database) | 
| 397 | 0 |     : m_database(database) | 
| 398 | 0 | { | 
| 399 |  |     // Make sure we have a db handle | 
| 400 | 0 |     assert(m_database.m_db); | 
| 401 |  |  | 
| 402 | 0 |     SetupSQLStatements(); | 
| 403 | 0 | } | 
| 404 |  |  | 
| 405 |  | void SQLiteBatch::Close() | 
| 406 | 0 | { | 
| 407 | 0 |     bool force_conn_refresh = false; | 
| 408 |  |  | 
| 409 |  |     // If we began a transaction, and it wasn't committed, abort the transaction in progress | 
| 410 | 0 |     if (m_txn) { | 
| 411 | 0 |         if (TxnAbort()) { | 
| 412 | 0 |             LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 413 | 0 |         } else { | 
| 414 |  |             // If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case | 
| 415 |  |             // by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction | 
| 416 |  |             // was opened will be rolled back and future transactions can succeed without committing old data. | 
| 417 | 0 |             force_conn_refresh = true; | 
| 418 | 0 |             LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 419 | 0 |         } | 
| 420 | 0 |     } | 
| 421 |  |  | 
| 422 |  |     // Free all of the prepared statements | 
| 423 | 0 |     const std::vector<std::pair<sqlite3_stmt**, const char*>> statements{ | 
| 424 | 0 |         {&m_read_stmt, "read"}, | 
| 425 | 0 |         {&m_insert_stmt, "insert"}, | 
| 426 | 0 |         {&m_overwrite_stmt, "overwrite"}, | 
| 427 | 0 |         {&m_delete_stmt, "delete"}, | 
| 428 | 0 |         {&m_delete_prefix_stmt, "delete prefix"}, | 
| 429 | 0 |     }; | 
| 430 |  | 
 | 
| 431 | 0 |     for (const auto& [stmt_prepared, stmt_description] : statements) { | 
| 432 | 0 |         int res = sqlite3_finalize(*stmt_prepared); | 
| 433 | 0 |         if (res != SQLITE_OK) { | 
| 434 | 0 |             LogPrintf("SQLiteBatch: Batch closed but could not finalize %s statement: %s\n",| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 435 | 0 |                       stmt_description, sqlite3_errstr(res)); | 
| 436 | 0 |         } | 
| 437 | 0 |         *stmt_prepared = nullptr; | 
| 438 | 0 |     } | 
| 439 |  | 
 | 
| 440 | 0 |     if (force_conn_refresh) { | 
| 441 | 0 |         m_database.Close(); | 
| 442 | 0 |         try { | 
| 443 | 0 |             m_database.Open(); | 
| 444 |  |             // If TxnAbort failed and we refreshed the connection, the semaphore was not released, so release it here to avoid deadlocks on future writes. | 
| 445 | 0 |             m_database.m_write_semaphore.release(); | 
| 446 | 0 |         } catch (const std::runtime_error&) { | 
| 447 |  |             // If open fails, cleanup this object and rethrow the exception | 
| 448 | 0 |             m_database.Close(); | 
| 449 | 0 |             throw; | 
| 450 | 0 |         } | 
| 451 | 0 |     } | 
| 452 | 0 | } | 
| 453 |  |  | 
| 454 |  | bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value) | 
| 455 | 0 | { | 
| 456 | 0 |     if (!m_database.m_db) return false; | 
| 457 | 0 |     assert(m_read_stmt); | 
| 458 |  |  | 
| 459 |  |     // Bind: leftmost parameter in statement is index 1 | 
| 460 | 0 |     if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false; | 
| 461 | 0 |     int res = sqlite3_step(m_read_stmt); | 
| 462 | 0 |     if (res != SQLITE_ROW) { | 
| 463 | 0 |         if (res != SQLITE_DONE) { | 
| 464 |  |             // SQLITE_DONE means "not found", don't log an error in that case. | 
| 465 | 0 |             LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 466 | 0 |         } | 
| 467 | 0 |         sqlite3_clear_bindings(m_read_stmt); | 
| 468 | 0 |         sqlite3_reset(m_read_stmt); | 
| 469 | 0 |         return false; | 
| 470 | 0 |     } | 
| 471 |  |     // Leftmost column in result is index 0 | 
| 472 | 0 |     value.clear(); | 
| 473 | 0 |     value.write(SpanFromBlob(m_read_stmt, 0)); | 
| 474 |  | 
 | 
| 475 | 0 |     sqlite3_clear_bindings(m_read_stmt); | 
| 476 | 0 |     sqlite3_reset(m_read_stmt); | 
| 477 | 0 |     return true; | 
| 478 | 0 | } | 
| 479 |  |  | 
| 480 |  | bool SQLiteBatch::WriteKey(DataStream&& key, DataStream&& value, bool overwrite) | 
| 481 | 0 | { | 
| 482 | 0 |     if (!m_database.m_db) return false; | 
| 483 | 0 |     assert(m_insert_stmt && m_overwrite_stmt); | 
| 484 |  |  | 
| 485 | 0 |     sqlite3_stmt* stmt; | 
| 486 | 0 |     if (overwrite) { | 
| 487 | 0 |         stmt = m_overwrite_stmt; | 
| 488 | 0 |     } else { | 
| 489 | 0 |         stmt = m_insert_stmt; | 
| 490 | 0 |     } | 
| 491 |  |  | 
| 492 |  |     // Bind: leftmost parameter in statement is index 1 | 
| 493 |  |     // Insert index 1 is key, 2 is value | 
| 494 | 0 |     if (!BindBlobToStatement(stmt, 1, key, "key")) return false; | 
| 495 | 0 |     if (!BindBlobToStatement(stmt, 2, value, "value")) return false; | 
| 496 |  |  | 
| 497 |  |     // Acquire semaphore if not previously acquired when creating a transaction. | 
| 498 | 0 |     if (!m_txn) m_database.m_write_semaphore.acquire(); | 
| 499 |  |  | 
| 500 |  |     // Execute | 
| 501 | 0 |     int res = sqlite3_step(stmt); | 
| 502 | 0 |     sqlite3_clear_bindings(stmt); | 
| 503 | 0 |     sqlite3_reset(stmt); | 
| 504 | 0 |     if (res != SQLITE_DONE) { | 
| 505 | 0 |         LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 506 | 0 |     } | 
| 507 |  | 
 | 
| 508 | 0 |     if (!m_txn) m_database.m_write_semaphore.release(); | 
| 509 |  | 
 | 
| 510 | 0 |     return res == SQLITE_DONE; | 
| 511 | 0 | } | 
| 512 |  |  | 
| 513 |  | bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, std::span<const std::byte> blob) | 
| 514 | 0 | { | 
| 515 | 0 |     if (!m_database.m_db) return false; | 
| 516 | 0 |     assert(stmt); | 
| 517 |  |  | 
| 518 |  |     // Bind: leftmost parameter in statement is index 1 | 
| 519 | 0 |     if (!BindBlobToStatement(stmt, 1, blob, "key")) return false; | 
| 520 |  |  | 
| 521 |  |     // Acquire semaphore if not previously acquired when creating a transaction. | 
| 522 | 0 |     if (!m_txn) m_database.m_write_semaphore.acquire(); | 
| 523 |  |  | 
| 524 |  |     // Execute | 
| 525 | 0 |     int res = sqlite3_step(stmt); | 
| 526 | 0 |     sqlite3_clear_bindings(stmt); | 
| 527 | 0 |     sqlite3_reset(stmt); | 
| 528 | 0 |     if (res != SQLITE_DONE) { | 
| 529 | 0 |         LogPrintf("%s: Unable to execute statement: %s\n", __func__, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 530 | 0 |     } | 
| 531 |  | 
 | 
| 532 | 0 |     if (!m_txn) m_database.m_write_semaphore.release(); | 
| 533 |  | 
 | 
| 534 | 0 |     return res == SQLITE_DONE; | 
| 535 | 0 | } | 
| 536 |  |  | 
| 537 |  | bool SQLiteBatch::EraseKey(DataStream&& key) | 
| 538 | 0 | { | 
| 539 | 0 |     return ExecStatement(m_delete_stmt, key); | 
| 540 | 0 | } | 
| 541 |  |  | 
| 542 |  | bool SQLiteBatch::ErasePrefix(std::span<const std::byte> prefix) | 
| 543 | 0 | { | 
| 544 | 0 |     return ExecStatement(m_delete_prefix_stmt, prefix); | 
| 545 | 0 | } | 
| 546 |  |  | 
| 547 |  | bool SQLiteBatch::HasKey(DataStream&& key) | 
| 548 | 0 | { | 
| 549 | 0 |     if (!m_database.m_db) return false; | 
| 550 | 0 |     assert(m_read_stmt); | 
| 551 |  |  | 
| 552 |  |     // Bind: leftmost parameter in statement is index 1 | 
| 553 | 0 |     if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false; | 
| 554 | 0 |     int res = sqlite3_step(m_read_stmt); | 
| 555 | 0 |     sqlite3_clear_bindings(m_read_stmt); | 
| 556 | 0 |     sqlite3_reset(m_read_stmt); | 
| 557 | 0 |     return res == SQLITE_ROW; | 
| 558 | 0 | } | 
| 559 |  |  | 
| 560 |  | DatabaseCursor::Status SQLiteCursor::Next(DataStream& key, DataStream& value) | 
| 561 | 0 | { | 
| 562 | 0 |     int res = sqlite3_step(m_cursor_stmt); | 
| 563 | 0 |     if (res == SQLITE_DONE) { | 
| 564 | 0 |         return Status::DONE; | 
| 565 | 0 |     } | 
| 566 | 0 |     if (res != SQLITE_ROW) { | 
| 567 | 0 |         LogPrintf("%s: Unable to execute cursor step: %s\n", __func__, sqlite3_errstr(res));| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 568 | 0 |         return Status::FAIL; | 
| 569 | 0 |     } | 
| 570 |  |  | 
| 571 | 0 |     key.clear(); | 
| 572 | 0 |     value.clear(); | 
| 573 |  |  | 
| 574 |  |     // Leftmost column in result is index 0 | 
| 575 | 0 |     key.write(SpanFromBlob(m_cursor_stmt, 0)); | 
| 576 | 0 |     value.write(SpanFromBlob(m_cursor_stmt, 1)); | 
| 577 | 0 |     return Status::MORE; | 
| 578 | 0 | } | 
| 579 |  |  | 
| 580 |  | SQLiteCursor::~SQLiteCursor() | 
| 581 | 0 | { | 
| 582 | 0 |     sqlite3_clear_bindings(m_cursor_stmt); | 
| 583 | 0 |     sqlite3_reset(m_cursor_stmt); | 
| 584 | 0 |     int res = sqlite3_finalize(m_cursor_stmt); | 
| 585 | 0 |     if (res != SQLITE_OK) { | 
| 586 | 0 |         LogPrintf("%s: cursor closed but could not finalize cursor statement: %s\n",| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 587 | 0 |                   __func__, sqlite3_errstr(res)); | 
| 588 | 0 |     } | 
| 589 | 0 | } | 
| 590 |  |  | 
| 591 |  | std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewCursor() | 
| 592 | 0 | { | 
| 593 | 0 |     if (!m_database.m_db) return nullptr; | 
| 594 | 0 |     auto cursor = std::make_unique<SQLiteCursor>(); | 
| 595 |  | 
 | 
| 596 | 0 |     const char* stmt_text = "SELECT key, value FROM main"; | 
| 597 | 0 |     int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr); | 
| 598 | 0 |     if (res != SQLITE_OK) { | 
| 599 | 0 |         throw std::runtime_error(strprintf( | Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 600 | 0 |             "%s: Failed to setup cursor SQL statement: %s\n", __func__, sqlite3_errstr(res))); | 
| 601 | 0 |     } | 
| 602 |  |  | 
| 603 | 0 |     return cursor; | 
| 604 | 0 | } | 
| 605 |  |  | 
| 606 |  | std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewPrefixCursor(std::span<const std::byte> prefix) | 
| 607 | 0 | { | 
| 608 | 0 |     if (!m_database.m_db) return nullptr; | 
| 609 |  |  | 
| 610 |  |     // To get just the records we want, the SQL statement does a comparison of the binary data | 
| 611 |  |     // where the data must be greater than or equal to the prefix, and less than | 
| 612 |  |     // the prefix incremented by one (when interpreted as an integer) | 
| 613 | 0 |     std::vector<std::byte> start_range(prefix.begin(), prefix.end()); | 
| 614 | 0 |     std::vector<std::byte> end_range(prefix.begin(), prefix.end()); | 
| 615 | 0 |     auto it = end_range.rbegin(); | 
| 616 | 0 |     for (; it != end_range.rend(); ++it) { | 
| 617 | 0 |         if (*it == std::byte(std::numeric_limits<unsigned char>::max())) { | 
| 618 | 0 |             *it = std::byte(0); | 
| 619 | 0 |             continue; | 
| 620 | 0 |         } | 
| 621 | 0 |         *it = std::byte(std::to_integer<unsigned char>(*it) + 1); | 
| 622 | 0 |         break; | 
| 623 | 0 |     } | 
| 624 | 0 |     if (it == end_range.rend()) { | 
| 625 |  |         // If the prefix is all 0xff bytes, clear end_range as we won't need it | 
| 626 | 0 |         end_range.clear(); | 
| 627 | 0 |     } | 
| 628 |  | 
 | 
| 629 | 0 |     auto cursor = std::make_unique<SQLiteCursor>(start_range, end_range); | 
| 630 | 0 |     if (!cursor) return nullptr; | 
| 631 |  |  | 
| 632 | 0 |     const char* stmt_text = end_range.empty() ? "SELECT key, value FROM main WHERE key >= ?" : | 
| 633 | 0 |                             "SELECT key, value FROM main WHERE key >= ? AND key < ?"; | 
| 634 | 0 |     int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr); | 
| 635 | 0 |     if (res != SQLITE_OK) { | 
| 636 | 0 |         throw std::runtime_error(strprintf( | Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 637 | 0 |             "SQLiteDatabase: Failed to setup cursor SQL statement: %s\n", sqlite3_errstr(res))); | 
| 638 | 0 |     } | 
| 639 |  |  | 
| 640 | 0 |     if (!BindBlobToStatement(cursor->m_cursor_stmt, 1, cursor->m_prefix_range_start, "prefix_start")) return nullptr; | 
| 641 | 0 |     if (!end_range.empty()) { | 
| 642 | 0 |         if (!BindBlobToStatement(cursor->m_cursor_stmt, 2, cursor->m_prefix_range_end, "prefix_end")) return nullptr; | 
| 643 | 0 |     } | 
| 644 |  |  | 
| 645 | 0 |     return cursor; | 
| 646 | 0 | } | 
| 647 |  |  | 
| 648 |  | bool SQLiteBatch::TxnBegin() | 
| 649 | 0 | { | 
| 650 | 0 |     if (!m_database.m_db || m_txn) return false; | 
| 651 | 0 |     m_database.m_write_semaphore.acquire(); | 
| 652 | 0 |     Assert(!m_database.HasActiveTxn()); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 653 | 0 |     int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION"); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 654 | 0 |     if (res != SQLITE_OK) { | 
| 655 | 0 |         LogPrintf("SQLiteBatch: Failed to begin the transaction\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 656 | 0 |         m_database.m_write_semaphore.release(); | 
| 657 | 0 |     } else { | 
| 658 | 0 |         m_txn = true; | 
| 659 | 0 |     } | 
| 660 | 0 |     return res == SQLITE_OK; | 
| 661 | 0 | } | 
| 662 |  |  | 
| 663 |  | bool SQLiteBatch::TxnCommit() | 
| 664 | 0 | { | 
| 665 | 0 |     if (!m_database.m_db || !m_txn) return false; | 
| 666 | 0 |     Assert(m_database.HasActiveTxn()); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 667 | 0 |     int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION"); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 668 | 0 |     if (res != SQLITE_OK) { | 
| 669 | 0 |         LogPrintf("SQLiteBatch: Failed to commit the transaction\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 670 | 0 |     } else { | 
| 671 | 0 |         m_txn = false; | 
| 672 | 0 |         m_database.m_write_semaphore.release(); | 
| 673 | 0 |     } | 
| 674 | 0 |     return res == SQLITE_OK; | 
| 675 | 0 | } | 
| 676 |  |  | 
| 677 |  | bool SQLiteBatch::TxnAbort() | 
| 678 | 0 | { | 
| 679 | 0 |     if (!m_database.m_db || !m_txn) return false; | 
| 680 | 0 |     Assert(m_database.HasActiveTxn()); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 681 | 0 |     int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION"); | Line | Count | Source |  | 106 | 0 | #define Assert(val) inline_assertion_check<true>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 682 | 0 |     if (res != SQLITE_OK) { | 
| 683 | 0 |         LogPrintf("SQLiteBatch: Failed to abort the transaction\n");| Line | Count | Source |  | 361 | 0 | #define LogPrintf(...) LogInfo(__VA_ARGS__) | Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
 | 
| 684 | 0 |     } else { | 
| 685 | 0 |         m_txn = false; | 
| 686 | 0 |         m_database.m_write_semaphore.release(); | 
| 687 | 0 |     } | 
| 688 | 0 |     return res == SQLITE_OK; | 
| 689 | 0 | } | 
| 690 |  |  | 
| 691 |  | std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error) | 
| 692 | 0 | { | 
| 693 | 0 |     try { | 
| 694 | 0 |         fs::path data_file = SQLiteDataFile(path); | 
| 695 | 0 |         auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options); | 
| 696 | 0 |         if (options.verify && !db->Verify(error)) { | 
| 697 | 0 |             status = DatabaseStatus::FAILED_VERIFY; | 
| 698 | 0 |             return nullptr; | 
| 699 | 0 |         } | 
| 700 | 0 |         status = DatabaseStatus::SUCCESS; | 
| 701 | 0 |         return db; | 
| 702 | 0 |     } catch (const std::runtime_error& e) { | 
| 703 | 0 |         status = DatabaseStatus::FAILED_LOAD; | 
| 704 | 0 |         error = Untranslated(e.what()); | 
| 705 | 0 |         return nullptr; | 
| 706 | 0 |     } | 
| 707 | 0 | } | 
| 708 |  |  | 
| 709 |  | std::string SQLiteDatabaseVersion() | 
| 710 | 0 | { | 
| 711 | 0 |     return std::string(sqlite3_libversion()); | 
| 712 | 0 | } | 
| 713 |  | } // namespace wallet |