upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBitflash <admin@bitflash.network>2026-08-14 15:35:42 -0300
committerBitflash <admin@bitflash.network>2026-08-14 15:35:42 -0300
commitc08a32d26cabf5acc9dabf298a22051596308174 (patch)
tree25c86213839210a08ff69983e7219e924d5dbb85 /src
parent9b281d890168c7c912857530d787fbc82203d848 (diff)
parentc49b92a9a79a398b51200e69f7dde8a467b9c925 (diff)
Merge pull request 'wallet: add experimental SQLite record store' (#47) from wallet-sqlite-skeleton into main
Diffstat (limited to 'src')
-rw-r--r--src/Makefile10
-rw-r--r--src/main_gui.cpp3
-rw-r--r--src/makefile.mingw8
-rw-r--r--src/selftest.cpp108
-rw-r--r--src/wallet_sqlite.cpp254
-rw-r--r--src/wallet_sqlite.h40
6 files changed, 414 insertions, 9 deletions
diff --git a/src/Makefile b/src/Makefile
index 19ad33a..cfa40f6 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -40,7 +40,7 @@ FUZZ_LDFLAGS = -fsanitize=fuzzer,address,undefined
40 40
41LIBS = -L$(SECP_LIB) -lsecp256k1 \ 41LIBS = -L$(SECP_LIB) -lsecp256k1 \
42 -L$(RANDOMX_DIR)/build -lrandomx \ 42 -L$(RANDOMX_DIR)/build -lrandomx \
43 -lsodium -lssl -lcrypto -ldb_cxx -lpthread \ 43 -lsodium -lssl -lcrypto -ldb_cxx -lsqlite3 -lpthread \
44 -lglfw -lGL 44 -lglfw -lGL
45 45
46# Every header a .cpp here includes has to be listed, not just the interesting 46# Every header a .cpp here includes has to be listed, not just the interesting
@@ -53,7 +53,7 @@ LIBS = -L$(SECP_LIB) -lsecp256k1 \
53HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \ 53HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \
54 key.h bignum.h script.h db.h base58.h net.h nostr.h \ 54 key.h bignum.h script.h db.h base58.h net.h nostr.h \
55 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \ 55 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \
56 selftest.h walletcmd.h compat.h sockcount.h proxy.h tor.h font_roboto.h 56 selftest.h walletcmd.h wallet_sqlite.h compat.h sockcount.h proxy.h tor.h font_roboto.h
57 57
58IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \ 58IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
59 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o 59 obj/imgui_widgets.o obj/imgui_impl_glfw.o obj/imgui_impl_opengl3.o
@@ -61,7 +61,8 @@ IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
61NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \ 61NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \
62 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \ 62 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \
63 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \ 63 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \
64 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/proxy.o obj/tor.o 64 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/wallet_sqlite.o \
65 obj/proxy.o obj/tor.o
65 66
66GUI_OBJS = obj/gui.o obj/main_gui.o 67GUI_OBJS = obj/gui.o obj/main_gui.o
67 68
@@ -116,7 +117,7 @@ bitflash: $(IMGUI_OBJS) $(NODE_OBJS) $(GUI_OBJS)
116 117
117NODE_LIBS = -L$(SECP_LIB) -lsecp256k1 \ 118NODE_LIBS = -L$(SECP_LIB) -lsecp256k1 \
118 -L$(RANDOMX_DIR)/build -lrandomx \ 119 -L$(RANDOMX_DIR)/build -lrandomx \
119 -lsodium -lssl -lcrypto -ldb_cxx -lpthread 120 -lsodium -lssl -lcrypto -ldb_cxx -lsqlite3 -lpthread
120 121
121NODE_ONLY_OBJS = $(patsubst obj/%,obj-node/%,$(NODE_OBJS)) \ 122NODE_ONLY_OBJS = $(patsubst obj/%,obj-node/%,$(NODE_OBJS)) \
122 obj-node/main_gui.o obj-node/headless.o 123 obj-node/main_gui.o obj-node/headless.o
@@ -187,6 +188,7 @@ tests: test_bip32 bitflash-node
187 ./bitflash-node -selftest=wallet-keypool 188 ./bitflash-node -selftest=wallet-keypool
188 ./bitflash-node -selftest=wallet-hd 189 ./bitflash-node -selftest=wallet-hd
189 ./bitflash-node -selftest=wallet-format 190 ./bitflash-node -selftest=wallet-format
191 ./bitflash-node -selftest=wallet-sqlite
190 ./bitflash-node -selftest=wallet-crypto 192 ./bitflash-node -selftest=wallet-crypto
191 ./bitflash-node -selftest=wallet-encrypt 193 ./bitflash-node -selftest=wallet-encrypt
192 ./bitflash-node -selftest=net-message 194 ./bitflash-node -selftest=net-message
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 705b4ef..965c818 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -114,7 +114,8 @@ static void PrintUsage()
114 printf(" /gen\n"); 114 printf(" /gen\n");
115 printf(" /nogui or /daemon\n"); 115 printf(" /nogui or /daemon\n");
116 printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); 116 printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n");
117 printf(" db-env-reopen, wallet-crypto, wallet-encrypt, wallet-portability,\n"); 117 printf(" db-env-reopen, wallet-sqlite, wallet-crypto, wallet-encrypt,\n");
118 printf(" wallet-portability,\n");
118 printf(" net-message,\n"); 119 printf(" net-message,\n");
119 printf(" consensus-limits, pool-stratum,\n"); 120 printf(" consensus-limits, pool-stratum,\n");
120 printf(" parse-money, socks5-proxy, or managed-tor\n"); 121 printf(" parse-money, socks5-proxy, or managed-tor\n");
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 333be96..2ff4e11 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -24,7 +24,7 @@ CXXFLAGS = -std=gnu++14 -O2 $(WARNFLAGS) -fpermissive -pthread \
24 24
25LIBS = -L$(DEPS)/lib -lsecp256k1 \ 25LIBS = -L$(DEPS)/lib -lsecp256k1 \
26 -L$(RANDOMX)/build -lrandomx \ 26 -L$(RANDOMX)/build -lrandomx \
27 -lsodium -lssl -lcrypto -ldb_cxx -lpthread \ 27 -lsodium -lssl -lcrypto -ldb_cxx -lsqlite3 -lpthread \
28 -LC:/msys64/ucrt64/lib \ 28 -LC:/msys64/ucrt64/lib \
29 -lglfw3 -lopengl32 -lgdi32 \ 29 -lglfw3 -lopengl32 -lgdi32 \
30 -lkernel32 -luser32 -lcomdlg32 -lwinspool -lwinmm \ 30 -lkernel32 -luser32 -lcomdlg32 -lwinspool -lwinmm \
@@ -37,7 +37,8 @@ IMGUI_OBJS = obj/imgui.o obj/imgui_draw.o obj/imgui_tables.o \
37NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \ 37NODE_OBJS = obj/util.o obj/script.o obj/db.o obj/net.o obj/main.o \
38 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \ 38 obj/market.o obj/sha.o obj/nostr.o obj/randomx_pow.o \
39 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \ 39 obj/btfaddr.o obj/btfchan.o obj/btfrv.o obj/btftunnel.o obj/rpc.o \
40 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/proxy.o obj/tor.o 40 obj/selftest.o obj/bip32.o obj/walletcmd.o obj/wallet_sqlite.o \
41 obj/proxy.o obj/tor.o
41 42
42GUI_OBJS = obj/gui.o obj/main_gui.o obj/bitflash_res.o 43GUI_OBJS = obj/gui.o obj/main_gui.o obj/bitflash_res.o
43 44
@@ -51,7 +52,7 @@ GUI_OBJS = obj/gui.o obj/main_gui.o obj/bitflash_res.o
51HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \ 52HEADERS = headers_core.h headers.h util.h main.h serialize.h uint256.h \
52 key.h bignum.h script.h db.h base58.h net.h nostr.h \ 53 key.h bignum.h script.h db.h base58.h net.h nostr.h \
53 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \ 54 randomx_pow.h btfaddr.h btfchan.h btfrv.h btftunnel.h market.h \
54 selftest.h walletcmd.h sockcount.h proxy.h tor.h compat.h font_roboto.h 55 selftest.h walletcmd.h wallet_sqlite.h sockcount.h proxy.h tor.h compat.h font_roboto.h
55 56
56all: bitflash.exe 57all: bitflash.exe
57 58
@@ -107,6 +108,7 @@ tests: test_bip32.exe bitflash.exe
107 ./bitflash.exe -selftest=wallet-keypool 108 ./bitflash.exe -selftest=wallet-keypool
108 ./bitflash.exe -selftest=wallet-hd 109 ./bitflash.exe -selftest=wallet-hd
109 ./bitflash.exe -selftest=wallet-format 110 ./bitflash.exe -selftest=wallet-format
111 ./bitflash.exe -selftest=wallet-sqlite
110 ./bitflash.exe -selftest=wallet-crypto 112 ./bitflash.exe -selftest=wallet-crypto
111 ./bitflash.exe -selftest=wallet-encrypt 113 ./bitflash.exe -selftest=wallet-encrypt
112 ./bitflash.exe -selftest=net-message 114 ./bitflash.exe -selftest=net-message
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 31c168f..f9c4a8e 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -13,6 +13,7 @@
13#include "sockcount.h" 13#include "sockcount.h"
14#include "tor.h" 14#include "tor.h"
15#include "walletcmd.h" 15#include "walletcmd.h"
16#include "wallet_sqlite.h"
16 17
17extern int RunPoolStratumSelfTest(); 18extern int RunPoolStratumSelfTest();
18 19
@@ -62,6 +63,11 @@ static std::string HexStrLocal(const std::vector<unsigned char>& v)
62 return out; 63 return out;
63} 64}
64 65
66static vector<unsigned char> DataStreamBytes(const CDataStream& ss)
67{
68 return vector<unsigned char>(ss.begin(), ss.end());
69}
70
65static bool MakeTempDir(std::string& out) 71static bool MakeTempDir(std::string& out)
66{ 72{
67#ifdef _WIN32 73#ifdef _WIN32
@@ -1030,6 +1036,104 @@ static int RunDbEnvReopenSelfTest()
1030 return nFail == 0 ? 0 : 1; 1036 return nFail == 0 ? 0 : 1;
1031} 1037}
1032 1038
1039static int RunWalletSQLiteSelfTest()
1040{
1041 fflush(stdout);
1042 printf("wallet-sqlite self-test\n");
1043
1044 std::string tmp;
1045 if (!MakeTempDir(tmp))
1046 {
1047 printf(" FAIL could not create a temporary data directory\n");
1048 return 1;
1049 }
1050
1051 int nFail = 0;
1052 string strPath = tmp + "/wallet.sqlite";
1053 string strError;
1054
1055 try
1056 {
1057 CDataStream ssKeySetting(SER_DISK);
1058 ssKeySetting << make_pair(string("setting"), string("sqlite-roundtrip"));
1059 CDataStream ssValueSetting(SER_DISK);
1060 ssValueSetting << (int64)424242;
1061
1062 vector<unsigned char> vchPubKey;
1063 vchPubKey.push_back(0x04);
1064 for (int i = 0; i < 64; i++)
1065 vchPubKey.push_back((unsigned char)i);
1066 CDataStream ssKeyDefault(SER_DISK);
1067 ssKeyDefault << string("defaultkey");
1068 CDataStream ssValueDefault(SER_DISK);
1069 ssValueDefault << vchPubKey;
1070
1071 {
1072 CWalletDBSQLite db;
1073 nFail += Check(db.Open(strPath, strError),
1074 "SQLite wallet opens and creates schema") ? 0 : 1;
1075 nFail += Check(db.BeginTransaction(strError),
1076 "SQLite wallet begins a write transaction") ? 0 : 1;
1077 nFail += Check(db.WriteRecord(DataStreamBytes(ssKeySetting),
1078 DataStreamBytes(ssValueSetting),
1079 strError),
1080 "SQLite wallet writes a serialized setting record") ? 0 : 1;
1081 nFail += Check(db.WriteRecord(DataStreamBytes(ssKeyDefault),
1082 DataStreamBytes(ssValueDefault),
1083 strError),
1084 "SQLite wallet writes a serialized defaultkey record") ? 0 : 1;
1085 nFail += Check(db.CommitTransaction(strError),
1086 "SQLite wallet commits a write transaction") ? 0 : 1;
1087 int nCount = 0;
1088 nFail += Check(db.CountRecords(nCount, strError) && nCount == 2,
1089 "SQLite wallet counts raw records") ? 0 : 1;
1090 }
1091
1092 {
1093 CWalletDBSQLite db;
1094 nFail += Check(db.Open(strPath, strError),
1095 "SQLite wallet reopens") ? 0 : 1;
1096 vector<unsigned char> vchRead;
1097 nFail += Check(db.ReadRecord(DataStreamBytes(ssKeySetting),
1098 vchRead,
1099 strError),
1100 "SQLite wallet reads a serialized setting record") ? 0 : 1;
1101 int64 nReadValue = 0;
1102 CDataStream ssRead(vchRead, SER_DISK);
1103 ssRead >> nReadValue;
1104 nFail += Check(!ssRead.fail() && nReadValue == 424242,
1105 "SQLite wallet preserves serialized values byte-for-byte") ? 0 : 1;
1106
1107 vchRead.clear();
1108 nFail += Check(db.ReadRecord(DataStreamBytes(ssKeyDefault),
1109 vchRead,
1110 strError),
1111 "SQLite wallet reads a serialized defaultkey record") ? 0 : 1;
1112 vector<unsigned char> vchReadPubKey;
1113 CDataStream ssReadDefault(vchRead, SER_DISK);
1114 ssReadDefault >> vchReadPubKey;
1115 nFail += Check(!ssReadDefault.fail() && vchReadPubKey == vchPubKey,
1116 "SQLite wallet preserves public key blobs") ? 0 : 1;
1117 }
1118 }
1119 catch (const std::exception& e)
1120 {
1121 printf(" FAIL exception: %s\n", e.what());
1122 nFail++;
1123 }
1124 catch (...)
1125 {
1126 printf(" FAIL unknown exception\n");
1127 nFail++;
1128 }
1129
1130 RemoveTree(tmp);
1131 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1132 nFail, nFail == 1 ? "" : "s");
1133 fflush(stdout);
1134 return nFail == 0 ? 0 : 1;
1135}
1136
1033static void ClearWalletRuntimeForTest() 1137static void ClearWalletRuntimeForTest()
1034{ 1138{
1035 CRITICAL_BLOCK(cs_mapKeys) 1139 CRITICAL_BLOCK(cs_mapKeys)
@@ -2289,6 +2393,8 @@ int RunSelfTest(const std::string& name)
2289 return RunWalletStorageSanitySelfTest(); 2393 return RunWalletStorageSanitySelfTest();
2290 if (name == "db-env-reopen") 2394 if (name == "db-env-reopen")
2291 return RunDbEnvReopenSelfTest(); 2395 return RunDbEnvReopenSelfTest();
2396 if (name == "wallet-sqlite")
2397 return RunWalletSQLiteSelfTest();
2292 if (name == "wallet-crypto") 2398 if (name == "wallet-crypto")
2293 return RunWalletCryptoSelfTest(); 2399 return RunWalletCryptoSelfTest();
2294 if (name == "wallet-encrypt") 2400 if (name == "wallet-encrypt")
@@ -2309,6 +2415,6 @@ int RunSelfTest(const std::string& name)
2309 return RunManagedTorSelfTest(); 2415 return RunManagedTorSelfTest();
2310 2416
2311 printf("Unknown self-test '%s'\n", name.c_str()); 2417 printf("Unknown self-test '%s'\n", name.c_str());
2312 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); 2418 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n");
2313 return 1; 2419 return 1;
2314} 2420}
diff --git a/src/wallet_sqlite.cpp b/src/wallet_sqlite.cpp
new file mode 100644
index 0000000..4104ac6
--- /dev/null
+++ b/src/wallet_sqlite.cpp
@@ -0,0 +1,254 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3
4#include "headers.h"
5#include "wallet_sqlite.h"
6
7static void SetSQLiteError(sqlite3* pdb, const string& strContext,
8 string& strError)
9{
10 if (pdb)
11 strError = strContext + ": " + sqlite3_errmsg(pdb);
12 else
13 strError = strContext;
14}
15
16CWalletDBSQLite::CWalletDBSQLite() : pdb(NULL)
17{
18}
19
20CWalletDBSQLite::~CWalletDBSQLite()
21{
22 Close();
23}
24
25bool CWalletDBSQLite::Open(const string& strPath, string& strError)
26{
27 Close();
28
29 int ret = sqlite3_open_v2(strPath.c_str(), &pdb,
30 SQLITE_OPEN_READWRITE |
31 SQLITE_OPEN_CREATE |
32 SQLITE_OPEN_FULLMUTEX,
33 NULL);
34 if (ret != SQLITE_OK)
35 {
36 SetSQLiteError(pdb, "cannot open SQLite wallet", strError);
37 Close();
38 return false;
39 }
40
41 sqlite3_busy_timeout(pdb, 5000);
42
43 if (!Exec("PRAGMA journal_mode=WAL;", strError))
44 {
45 Close();
46 return false;
47 }
48 if (!Exec("PRAGMA synchronous=FULL;", strError))
49 {
50 Close();
51 return false;
52 }
53 if (!InitSchema(strError))
54 {
55 Close();
56 return false;
57 }
58 return true;
59}
60
61void CWalletDBSQLite::Close()
62{
63 if (pdb)
64 {
65 sqlite3_close(pdb);
66 pdb = NULL;
67 }
68}
69
70bool CWalletDBSQLite::BeginTransaction(string& strError)
71{
72 if (!pdb)
73 {
74 strError = "SQLite wallet is not open";
75 return false;
76 }
77 return Exec("BEGIN IMMEDIATE;", strError);
78}
79
80bool CWalletDBSQLite::CommitTransaction(string& strError)
81{
82 if (!pdb)
83 {
84 strError = "SQLite wallet is not open";
85 return false;
86 }
87 return Exec("COMMIT;", strError);
88}
89
90bool CWalletDBSQLite::RollbackTransaction(string& strError)
91{
92 if (!pdb)
93 {
94 strError = "SQLite wallet is not open";
95 return false;
96 }
97 return Exec("ROLLBACK;", strError);
98}
99
100bool CWalletDBSQLite::Exec(const char* pszSql, string& strError)
101{
102 char* pszErr = NULL;
103 int ret = sqlite3_exec(pdb, pszSql, NULL, NULL, &pszErr);
104 if (ret != SQLITE_OK)
105 {
106 if (pszErr)
107 {
108 strError = pszErr;
109 sqlite3_free(pszErr);
110 }
111 else
112 SetSQLiteError(pdb, "SQLite exec failed", strError);
113 return false;
114 }
115 return true;
116}
117
118bool CWalletDBSQLite::InitSchema(string& strError)
119{
120 return Exec("CREATE TABLE IF NOT EXISTS wallet_records ("
121 "key BLOB PRIMARY KEY NOT NULL,"
122 "value BLOB NOT NULL"
123 ");",
124 strError) &&
125 Exec("PRAGMA user_version=1;", strError);
126}
127
128bool CWalletDBSQLite::WriteRecord(const vector<unsigned char>& vchKey,
129 const vector<unsigned char>& vchValue,
130 string& strError)
131{
132 if (!pdb)
133 {
134 strError = "SQLite wallet is not open";
135 return false;
136 }
137 if (vchKey.empty())
138 {
139 strError = "SQLite wallet record key is empty";
140 return false;
141 }
142
143 sqlite3_stmt* stmt = NULL;
144 const char* pszSql =
145 "INSERT OR REPLACE INTO wallet_records(key, value) VALUES(?, ?);";
146 int ret = sqlite3_prepare_v2(pdb, pszSql, -1, &stmt, NULL);
147 if (ret != SQLITE_OK)
148 {
149 SetSQLiteError(pdb, "cannot prepare SQLite wallet write", strError);
150 return false;
151 }
152
153 ret = sqlite3_bind_blob(stmt, 1, &vchKey[0], (int)vchKey.size(),
154 SQLITE_TRANSIENT);
155 if (ret == SQLITE_OK)
156 ret = sqlite3_bind_blob(stmt, 2,
157 vchValue.empty() ? NULL : &vchValue[0],
158 (int)vchValue.size(),
159 SQLITE_TRANSIENT);
160 if (ret == SQLITE_OK)
161 ret = sqlite3_step(stmt);
162
163 bool fOk = (ret == SQLITE_DONE);
164 if (!fOk)
165 SetSQLiteError(pdb, "cannot write SQLite wallet record", strError);
166 sqlite3_finalize(stmt);
167 return fOk;
168}
169
170bool CWalletDBSQLite::ReadRecord(const vector<unsigned char>& vchKey,
171 vector<unsigned char>& vchValueRet,
172 string& strError)
173{
174 vchValueRet.clear();
175 if (!pdb)
176 {
177 strError = "SQLite wallet is not open";
178 return false;
179 }
180 if (vchKey.empty())
181 {
182 strError = "SQLite wallet record key is empty";
183 return false;
184 }
185
186 sqlite3_stmt* stmt = NULL;
187 const char* pszSql = "SELECT value FROM wallet_records WHERE key = ?;";
188 int ret = sqlite3_prepare_v2(pdb, pszSql, -1, &stmt, NULL);
189 if (ret != SQLITE_OK)
190 {
191 SetSQLiteError(pdb, "cannot prepare SQLite wallet read", strError);
192 return false;
193 }
194
195 ret = sqlite3_bind_blob(stmt, 1, &vchKey[0], (int)vchKey.size(),
196 SQLITE_TRANSIENT);
197 if (ret != SQLITE_OK)
198 {
199 SetSQLiteError(pdb, "cannot bind SQLite wallet key", strError);
200 sqlite3_finalize(stmt);
201 return false;
202 }
203
204 ret = sqlite3_step(stmt);
205 if (ret == SQLITE_ROW)
206 {
207 const unsigned char* pch =
208 (const unsigned char*)sqlite3_column_blob(stmt, 0);
209 int nBytes = sqlite3_column_bytes(stmt, 0);
210 if (nBytes > 0 && pch)
211 vchValueRet.assign(pch, pch + nBytes);
212 sqlite3_finalize(stmt);
213 return true;
214 }
215
216 if (ret == SQLITE_DONE)
217 strError = "SQLite wallet record not found";
218 else
219 SetSQLiteError(pdb, "cannot read SQLite wallet record", strError);
220 sqlite3_finalize(stmt);
221 return false;
222}
223
224bool CWalletDBSQLite::CountRecords(int& nRecordsRet, string& strError)
225{
226 nRecordsRet = 0;
227 if (!pdb)
228 {
229 strError = "SQLite wallet is not open";
230 return false;
231 }
232
233 sqlite3_stmt* stmt = NULL;
234 int ret = sqlite3_prepare_v2(pdb,
235 "SELECT COUNT(*) FROM wallet_records;",
236 -1, &stmt, NULL);
237 if (ret != SQLITE_OK)
238 {
239 SetSQLiteError(pdb, "cannot prepare SQLite wallet count", strError);
240 return false;
241 }
242
243 ret = sqlite3_step(stmt);
244 if (ret != SQLITE_ROW)
245 {
246 SetSQLiteError(pdb, "cannot count SQLite wallet records", strError);
247 sqlite3_finalize(stmt);
248 return false;
249 }
250
251 nRecordsRet = sqlite3_column_int(stmt, 0);
252 sqlite3_finalize(stmt);
253 return true;
254}
diff --git a/src/wallet_sqlite.h b/src/wallet_sqlite.h
new file mode 100644
index 0000000..9812d3a
--- /dev/null
+++ b/src/wallet_sqlite.h
@@ -0,0 +1,40 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3
4#ifndef BITFLASH_WALLET_SQLITE_H
5#define BITFLASH_WALLET_SQLITE_H
6
7#include <sqlite3.h>
8
9class CWalletDBSQLite
10{
11public:
12 CWalletDBSQLite();
13 ~CWalletDBSQLite();
14
15 bool Open(const string& strPath, string& strError);
16 void Close();
17
18 bool BeginTransaction(string& strError);
19 bool CommitTransaction(string& strError);
20 bool RollbackTransaction(string& strError);
21
22 bool WriteRecord(const vector<unsigned char>& vchKey,
23 const vector<unsigned char>& vchValue,
24 string& strError);
25 bool ReadRecord(const vector<unsigned char>& vchKey,
26 vector<unsigned char>& vchValueRet,
27 string& strError);
28 bool CountRecords(int& nRecordsRet, string& strError);
29
30private:
31 sqlite3* pdb;
32
33 bool Exec(const char* pszSql, string& strError);
34 bool InitSchema(string& strError);
35
36 CWalletDBSQLite(const CWalletDBSQLite&);
37 void operator=(const CWalletDBSQLite&);
38};
39
40#endif