diff options
| author | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-14 16:20:41 -0300 |
|---|---|---|
| committer | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-14 16:20:41 -0300 |
| commit | df84b82162b38dcd57114510b9651af5d85f04cb (patch) | |
| tree | 37d252761127383f5f8eab98a0e57f33605e1fc0 | |
| parent | 25b1eb8e17bb4012a59166667c7e37564a2af939 (diff) | |
wallet: scan SQLite wallet records
| -rw-r--r-- | docs/storage.md | 4 | ||||
| -rw-r--r-- | src/selftest.cpp | 6 | ||||
| -rw-r--r-- | src/wallet_sqlite.cpp | 54 | ||||
| -rw-r--r-- | src/wallet_sqlite.h | 3 |
4 files changed, 66 insertions, 1 deletions
diff --git a/docs/storage.md b/docs/storage.md index bb414b5..2d748fa 100644 --- a/docs/storage.md +++ b/docs/storage.md | |||
| @@ -108,7 +108,9 @@ BDB-to-SQLite migration or runtime backend flag exists. | |||
| 108 | `wallet.dat` record through `ScanWalletRecords()` and writes those same key and | 108 | `wallet.dat` record through `ScanWalletRecords()` and writes those same key and |
| 109 | value bytes into the SQLite table inside one transaction. It refuses to | 109 | value bytes into the SQLite table inside one transaction. It refuses to |
| 110 | overwrite an existing export and still does not change the runtime wallet | 110 | overwrite an existing export and still does not change the runtime wallet |
| 111 | backend. | 111 | backend. `CWalletDBSQLite::ScanRecords()` can stream those exported records back |
| 112 | through the same visitor shape, so tests can compare BDB and SQLite record | ||
| 113 | streams without introducing a SQLite runtime wallet loader yet. | ||
| 112 | 114 | ||
| 113 | ## `blkindex.dat` | 115 | ## `blkindex.dat` |
| 114 | 116 | ||
diff --git a/src/selftest.cpp b/src/selftest.cpp index 7a54271..408dd41 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1259,6 +1259,12 @@ static int RunWalletSQLiteMigrationSelfTest() | |||
| 1259 | } | 1259 | } |
| 1260 | nFail += Check(fAllMatch, | 1260 | nFail += Check(fAllMatch, |
| 1261 | "SQLite export preserves every wallet.dat key/value byte-for-byte") ? 0 : 1; | 1261 | "SQLite export preserves every wallet.dat key/value byte-for-byte") ? 0 : 1; |
| 1262 | |||
| 1263 | std::map<vector<unsigned char>, vector<unsigned char> > mapSQLite; | ||
| 1264 | CWalletRecordMapVisitor sqliteVisitor(mapSQLite); | ||
| 1265 | nFail += Check(db.ScanRecords(sqliteVisitor, strError) && | ||
| 1266 | mapSQLite == mapBDB, | ||
| 1267 | "SQLite wallet scanner streams every exported record byte-for-byte") ? 0 : 1; | ||
| 1262 | } | 1268 | } |
| 1263 | catch (const std::exception& e) | 1269 | catch (const std::exception& e) |
| 1264 | { | 1270 | { |
diff --git a/src/wallet_sqlite.cpp b/src/wallet_sqlite.cpp index 4369af6..8489cac 100644 --- a/src/wallet_sqlite.cpp +++ b/src/wallet_sqlite.cpp | |||
| @@ -231,6 +231,60 @@ bool CWalletDBSQLite::ReadRecord(const vector<unsigned char>& vchKey, | |||
| 231 | return false; | 231 | return false; |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | bool CWalletDBSQLite::ScanRecords(CWalletRecordVisitor& visitor, | ||
| 235 | string& strError) | ||
| 236 | { | ||
| 237 | if (!pdb) | ||
| 238 | { | ||
| 239 | strError = "SQLite wallet is not open"; | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | |||
| 243 | sqlite3_stmt* stmt = NULL; | ||
| 244 | const char* pszSql = "SELECT key, value FROM wallet_records ORDER BY rowid;"; | ||
| 245 | int ret = sqlite3_prepare_v2(pdb, pszSql, -1, &stmt, NULL); | ||
| 246 | if (ret != SQLITE_OK) | ||
| 247 | { | ||
| 248 | SetSQLiteError(pdb, "cannot prepare SQLite wallet scan", strError); | ||
| 249 | return false; | ||
| 250 | } | ||
| 251 | |||
| 252 | while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) | ||
| 253 | { | ||
| 254 | const unsigned char* pchKey = | ||
| 255 | (const unsigned char*)sqlite3_column_blob(stmt, 0); | ||
| 256 | int nKeyBytes = sqlite3_column_bytes(stmt, 0); | ||
| 257 | const unsigned char* pchValue = | ||
| 258 | (const unsigned char*)sqlite3_column_blob(stmt, 1); | ||
| 259 | int nValueBytes = sqlite3_column_bytes(stmt, 1); | ||
| 260 | |||
| 261 | vector<unsigned char> vchKey; | ||
| 262 | vector<unsigned char> vchValue; | ||
| 263 | if (nKeyBytes > 0 && pchKey) | ||
| 264 | vchKey.assign(pchKey, pchKey + nKeyBytes); | ||
| 265 | if (nValueBytes > 0 && pchValue) | ||
| 266 | vchValue.assign(pchValue, pchValue + nValueBytes); | ||
| 267 | |||
| 268 | CDataStream ssKey(vchKey, SER_DISK); | ||
| 269 | CDataStream ssValue(vchValue, SER_DISK); | ||
| 270 | if (!visitor.VisitWalletRecord(ssKey, ssValue, strError)) | ||
| 271 | { | ||
| 272 | sqlite3_finalize(stmt); | ||
| 273 | return false; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | if (ret != SQLITE_DONE) | ||
| 278 | { | ||
| 279 | SetSQLiteError(pdb, "cannot scan SQLite wallet records", strError); | ||
| 280 | sqlite3_finalize(stmt); | ||
| 281 | return false; | ||
| 282 | } | ||
| 283 | |||
| 284 | sqlite3_finalize(stmt); | ||
| 285 | return true; | ||
| 286 | } | ||
| 287 | |||
| 234 | bool CWalletDBSQLite::CountRecords(int& nRecordsRet, string& strError) | 288 | bool CWalletDBSQLite::CountRecords(int& nRecordsRet, string& strError) |
| 235 | { | 289 | { |
| 236 | nRecordsRet = 0; | 290 | nRecordsRet = 0; |
diff --git a/src/wallet_sqlite.h b/src/wallet_sqlite.h index 5ef862d..385a340 100644 --- a/src/wallet_sqlite.h +++ b/src/wallet_sqlite.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <sqlite3.h> | 7 | #include <sqlite3.h> |
| 8 | 8 | ||
| 9 | class CWalletRecordVisitor; | ||
| 10 | |||
| 9 | class CWalletDBSQLite | 11 | class CWalletDBSQLite |
| 10 | { | 12 | { |
| 11 | public: | 13 | public: |
| @@ -26,6 +28,7 @@ public: | |||
| 26 | bool ReadRecord(const vector<unsigned char>& vchKey, | 28 | bool ReadRecord(const vector<unsigned char>& vchKey, |
| 27 | vector<unsigned char>& vchValueRet, | 29 | vector<unsigned char>& vchValueRet, |
| 28 | string& strError); | 30 | string& strError); |
| 31 | bool ScanRecords(CWalletRecordVisitor& visitor, string& strError); | ||
| 29 | bool CountRecords(int& nRecordsRet, string& strError); | 32 | bool CountRecords(int& nRecordsRet, string& strError); |
| 30 | 33 | ||
| 31 | private: | 34 | private: |