Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
501sqlite3_stmt* stmt;502if (overwrite) {503stmt = m_overwrite_stmt;504} else {505stmt = m_insert_stmt;506}508// Bind: leftmost parameter in statement is index 1509// Insert index 1 is key, 2 is value510if (!BindBlobToStatement(stmt, 1, key, "key")) return false;511if (!BindBlobToStatement(stmt, 2, value, "value")) return false;513// Acquire semaphore if not previously acquired when creating a transaction.514if (!m_txn) m_database.m_write_semaphore.acquire();516// Execute517int res = sqlite3_step(stmt);518sqlite3_clear_bindings(stmt);519sqlite3_reset(stmt);520if (res != SQLITE_DONE) {521LogWarning("Unable to execute write statement: %s", sqlite3_errstr(res));522}524if (!m_txn) m_database.m_write_semaphore.release();526return res == SQLITE_DONE;527}529bool SQLiteBatch::ExecStatement(sqlite3_stmt* stmt, std::span<const std::byte> blob)530{531if (!m_database.m_db) return false;532assert(stmt);534// Bind: leftmost parameter in statement is index 1535if (!BindBlobToStatement(stmt, 1, blob, "key")) return false;537// Acquire semaphore if not previously acquired when creating a transaction.538if (!m_txn) m_database.m_write_semaphore.acquire();540// Execute541int res = sqlite3_step(stmt);542sqlite3_clear_bindings(stmt);543sqlite3_reset(stmt);544if (res != SQLITE_DONE) {545LogWarning("Unable to execute exec statement: %s", sqlite3_errstr(res));546}548if (!m_txn) m_database.m_write_semaphore.release();550return res == SQLITE_DONE;551}553bool SQLiteBatch::EraseKey(DataStream&& key)554{555return ExecStatement(m_delete_stmt, key);556}558bool SQLiteBatch::ErasePrefix(std::span<const std::byte> prefix)559{560return ExecStatement(m_delete_prefix_stmt, prefix);561}563bool SQLiteBatch::HasKey(DataStream&& key)564{565if (!m_database.m_db) return false;566assert(m_read_stmt);568// Bind: leftmost parameter in statement is index 1569if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false;570int res = sqlite3_step(m_read_stmt);571sqlite3_clear_bindings(m_read_stmt);572sqlite3_reset(m_read_stmt);573return res == SQLITE_ROW;574}576DatabaseCursor::Status SQLiteCursor::Next(DataStream& key, DataStream& value)577{578int res = sqlite3_step(m_cursor_stmt);579if (res == SQLITE_DONE) {580return Status::DONE;581}582if (res != SQLITE_ROW) {583LogWarning("Unable to execute cursor step: %s", sqlite3_errstr(res));584return Status::FAIL;585}587key.clear();588value.clear();590// Leftmost column in result is index 0591key.write(SpanFromBlob(m_cursor_stmt, 0));592value.write(SpanFromBlob(m_cursor_stmt, 1));593return Status::MORE;594}596SQLiteCursor::~SQLiteCursor()597{598sqlite3_clear_bindings(m_cursor_stmt);599sqlite3_reset(m_cursor_stmt);600int res = sqlite3_finalize(m_cursor_stmt);601if (res != SQLITE_OK) {602LogWarning("Cursor closed but could not finalize cursor statement: %s",603sqlite3_errstr(res));604}605}607std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewCursor()608{609if (!m_database.m_db) return nullptr;610auto cursor = std::make_unique<SQLiteCursor>();612const char* stmt_text = "SELECT key, value FROM main";613int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr);614if (res != SQLITE_OK) {615throw std::runtime_error(strprintf(616"%s: Failed to setup cursor SQL statement: %s\n", __func__, sqlite3_errstr(res)));617}619return cursor;620}622std::unique_ptr<DatabaseCursor> SQLiteBatch::GetNewPrefixCursor(std::span<const std::byte> prefix)623{624if (!m_database.m_db) return nullptr;626// To get just the records we want, the SQL statement does a comparison of the binary data627// where the data must be greater than or equal to the prefix, and less than628// the prefix incremented by one (when interpreted as an integer)629std::vector<std::byte> start_range(prefix.begin(), prefix.end());630std::vector<std::byte> end_range(prefix.begin(), prefix.end());631auto it = end_range.rbegin();632for (; it != end_range.rend(); ++it) {633if (*it == std::byte(std::numeric_limits<unsigned char>::max())) {634*it = std::byte(0);635continue;636}637*it = std::byte(std::to_integer<unsigned char>(*it) + 1);638break;639}640if (it == end_range.rend()) {641// If the prefix is all 0xff bytes, clear end_range as we won't need it642end_range.clear();643}645auto cursor = std::make_unique<SQLiteCursor>(start_range, end_range);646if (!cursor) return nullptr;648const char* stmt_text = end_range.empty() ? "SELECT key, value FROM main WHERE key >= ?" :649"SELECT key, value FROM main WHERE key >= ? AND key < ?";650int res = sqlite3_prepare_v2(m_database.m_db, stmt_text, -1, &cursor->m_cursor_stmt, nullptr);651if (res != SQLITE_OK) {652throw std::runtime_error(strprintf(653"SQLiteDatabase: Failed to setup cursor SQL statement: %s\n", sqlite3_errstr(res)));654}656if (!BindBlobToStatement(cursor->m_cursor_stmt, 1, cursor->m_prefix_range_start, "prefix_start")) return nullptr;657if (!end_range.empty()) {658if (!BindBlobToStatement(cursor->m_cursor_stmt, 2, cursor->m_prefix_range_end, "prefix_end")) return nullptr;659}661return cursor;662}664bool SQLiteBatch::TxnBegin()665{666if (!m_database.m_db || m_txn) return false;667m_database.m_write_semaphore.acquire();668Assert(!m_database.HasActiveTxn());669int res = Assert(m_exec_handler)->Exec(m_database, "BEGIN TRANSACTION");670if (res != SQLITE_OK) {671LogWarning("SQLiteBatch: Failed to begin the transaction");672m_database.m_write_semaphore.release();673} else {674m_txn = true;675}676return res == SQLITE_OK;677}679bool SQLiteBatch::TxnCommit()680{681if (!m_database.m_db || !m_txn) return false;682Assert(m_database.HasActiveTxn());683int res = Assert(m_exec_handler)->Exec(m_database, "COMMIT TRANSACTION");684if (res != SQLITE_OK) {685LogWarning("SQLiteBatch: Failed to commit the transaction");686} else {687m_txn = false;688m_database.m_write_semaphore.release();689}690return res == SQLITE_OK;691}693bool SQLiteBatch::TxnAbort()694{695if (!m_database.m_db || !m_txn) return false;696Assert(m_database.HasActiveTxn());697int res = Assert(m_exec_handler)->Exec(m_database, "ROLLBACK TRANSACTION");698if (res != SQLITE_OK) {699LogWarning("SQLiteBatch: Failed to abort the transaction");700} else {701m_txn = false;702m_database.m_write_semaphore.release();703}704return res == SQLITE_OK;705}707std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)708{709try {710fs::path data_file = SQLiteDataFile(path);711auto db = std::make_unique<SQLiteDatabase>(data_file.parent_path(), data_file, options);712if (options.verify && !db->Verify(error)) {713status = DatabaseStatus::FAILED_VERIFY;714return nullptr;715}716status = DatabaseStatus::SUCCESS;717return db;718} catch (const std::runtime_error& e) {719status = DatabaseStatus::FAILED_LOAD;720error = Untranslated(e.what());721return nullptr;722}723}725InMemoryWalletDatabase::InMemoryWalletDatabase()726: SQLiteDatabase(fs::path{}, fs::path{":memory:"}, DatabaseOptions(), SQLITE_OPEN_MEMORY)727{}729std::unique_ptr<WalletDatabase> MakeInMemoryWalletDatabase()730{731return std::make_unique<InMemoryWalletDatabase>();732}734std::string SQLiteDatabaseVersion()735{736return std::string(sqlite3_libversion());737}738} // namespace wallet