upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/wallet_sqlite.cpp
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 15:03:21 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-14 15:20:54 -0300
commitc49b92a9a79a398b51200e69f7dde8a467b9c925 (patch)
tree25c86213839210a08ff69983e7219e924d5dbb85 /src/wallet_sqlite.cpp
parent9b281d890168c7c912857530d787fbc82203d848 (diff)
wallet: add experimental SQLite record store
Diffstat (limited to 'src/wallet_sqlite.cpp')
-rw-r--r--src/wallet_sqlite.cpp254
1 files changed, 254 insertions, 0 deletions
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}