Skip to content

Commit fc0e747

Browse files
committed
sqlite: guard against dangling to-be-reverted db transactions
If the handler that initiated the database transaction is destroyed, the ongoing transaction cannot be left dangling when the db txn fails to abort. It must be forcefully reversed; otherwise, any subsequent db handler executing a write operation will dump the dangling, to-be-reverted transaction data to disk. This not only breaks the database isolation property but also results in the improper storage of incomplete information on disk, impacting the wallet consistency.
1 parent 472d2ca commit fc0e747

File tree

1 file changed

+18
-1
lines changed

1 file changed

+18
-1
lines changed

src/wallet/sqlite.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,12 +405,18 @@ SQLiteBatch::SQLiteBatch(SQLiteDatabase& database)
405405

406406
void SQLiteBatch::Close()
407407
{
408+
bool force_conn_refresh = false;
409+
408410
// If we began a transaction, and it wasn't committed, abort the transaction in progress
409411
if (m_database.HasActiveTxn()) {
410412
if (TxnAbort()) {
411413
LogPrintf("SQLiteBatch: Batch closed unexpectedly without the transaction being explicitly committed or aborted\n");
412414
} else {
413-
LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction\n");
415+
// If transaction cannot be aborted, it means there is a bug or there has been data corruption. Try to recover in this case
416+
// by closing and reopening the database. Closing the database should also ensure that any changes made since the transaction
417+
// was opened will be rolled back and future transactions can succeed without committing old data.
418+
force_conn_refresh = true;
419+
LogPrintf("SQLiteBatch: Batch closed and failed to abort transaction, resetting db connection..\n");
414420
}
415421
}
416422

@@ -431,6 +437,17 @@ void SQLiteBatch::Close()
431437
}
432438
*stmt_prepared = nullptr;
433439
}
440+
441+
if (force_conn_refresh) {
442+
m_database.Close();
443+
try {
444+
m_database.Open();
445+
} catch (const std::runtime_error&) {
446+
// If open fails, cleanup this object and rethrow the exception
447+
m_database.Close();
448+
throw;
449+
}
450+
}
434451
}
435452

436453
bool SQLiteBatch::ReadKey(DataStream&& key, DataStream& value)

0 commit comments

Comments
 (0)