From c2e05e13055da7377de43a9cbbe3335ce31e1268 Mon Sep 17 00:00:00 2001 From: Krystie Date: Sat, 4 Jul 2026 14:18:58 -0700 Subject: [PATCH] walletdb: fix SQLite cursor scan dropping accounting entries ListAccountCreditDebit kept the Berkeley-era early-break on the first non-acentry record. The BDB cursor was sorted and pre-seeked to the (acentry, account) prefix via DB_SET_RANGE, so breaking was correct there. The SQLite cursor (SELECT key, value FROM main) scans the whole keyspace in unspecified order, so the loop usually hit the version record first and returned zero entries: every wallet silently lost its accounting history in the UI. Skip non-matching records instead of breaking. Fixes all 27 accounting_tests/acc_orderupgrade failures. --- src/walletdb.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 61f8848..5fe9aea 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -168,17 +168,19 @@ void CWalletDB::ListAccountCreditDebit(const std::string& strAccount, std::list< break; } - // Unserialize. We mirror the Berkeley read: stop at the first non-acentry - // record (which is the next record type in key order — Berkeley's - // DB_SET_RANGE/DB_NEXT loop also terminated when the prefix changed). + // Unserialize. Unlike the Berkeley cursor -- which iterated in sorted + // key order and was positioned at the ("acentry", strAccount) prefix + // via DB_SET_RANGE, so it could stop at the first non-matching record -- + // the SQLite cursor scans the whole keyspace in unspecified order. + // We must therefore skip non-matching records and keep scanning. std::string strType; ssKey >> strType; if (strType != "acentry") - break; + continue; CAccountingEntry acentry; ssKey >> acentry.strAccount; if (!fAllAccounts && acentry.strAccount != strAccount) - break; + continue; ssValue >> acentry; ssKey >> acentry.nEntryNo;