1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #ifndef BITCOIN_WALLET_DB_H
7 #define BITCOIN_WALLET_DB_H
8
9 #include <clientversion.h>
10 #include <fs.h>
11 #include <streams.h>
12 #include <support/allocators/secure.h>
ListDatabases(const fs::path & wallet_dir)13
14 #include <atomic>
15 #include <memory>
16 #include <optional>
17 #include <string>
18
19 struct bilingual_str;
20
21 void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename);
22
23 /** RAII class that provides access to a WalletDatabase */
24 class DatabaseBatch
25 {
26 private:
27 virtual bool ReadKey(CDataStream&& key, CDataStream& value) = 0;
28 virtual bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) = 0;
29 virtual bool EraseKey(CDataStream&& key) = 0;
30 virtual bool HasKey(CDataStream&& key) = 0;
31
32 public:
33 explicit DatabaseBatch() {}
34 virtual ~DatabaseBatch() {}
35
36 DatabaseBatch(const DatabaseBatch&) = delete;
37 DatabaseBatch& operator=(const DatabaseBatch&) = delete;
38
39 virtual void Flush() = 0;
40 virtual void Close() = 0;
41
42 template <typename K, typename T>
43 bool Read(const K& key, T& value)
44 {
45 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
46 ssKey.reserve(1000);
47 ssKey << key;
48
49 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
50 if (!ReadKey(std::move(ssKey), ssValue)) return false;
51 try {
52 ssValue >> value;
53 return true;
54 } catch (const std::exception&) {
55 return false;
56 }
57 }
58
59 template <typename K, typename T>
60 bool Write(const K& key, const T& value, bool fOverwrite = true)
BDBDataFile(const fs::path & wallet_path)61 {
62 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
63 ssKey.reserve(1000);
64 ssKey << key;
65
66 CDataStream ssValue(SER_DISK, CLIENT_VERSION);
67 ssValue.reserve(10000);
68 ssValue << value;
69
70 return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
71 }
72
73 template <typename K>
74 bool Erase(const K& key)
SQLiteDataFile(const fs::path & path)75 {
76 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
77 ssKey.reserve(1000);
78 ssKey << key;
79
80 return EraseKey(std::move(ssKey));
81 }
82
83 template <typename K>
84 bool Exists(const K& key)
85 {
86 CDataStream ssKey(SER_DISK, CLIENT_VERSION);
87 ssKey.reserve(1000);
88 ssKey << key;
89
90 return HasKey(std::move(ssKey));
91 }
92
93 virtual bool StartCursor() = 0;
94 virtual bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) = 0;
95 virtual void CloseCursor() = 0;
96 virtual bool TxnBegin() = 0;
97 virtual bool TxnCommit() = 0;
98 virtual bool TxnAbort() = 0;
99 };
100
101 /** An instance of this class represents one database.
102 **/
103 class WalletDatabase
104 {
IsSQLiteFile(const fs::path & path)105 public:
106 /** Create dummy DB handle */
107 WalletDatabase() : nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0) {}
108 virtual ~WalletDatabase() {};
109
110 /** Open the database if it is not already opened. */
111 virtual void Open() = 0;
112
113 //! Counts the number of active database users to be sure that the database is not closed while someone is using it
114 std::atomic<int> m_refcount{0};
115 /** Indicate the a new database user has began using the database. Increments m_refcount */
116 virtual void AddRef() = 0;
117 /** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */
118 virtual void RemoveRef() = 0;
119
120 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
121 */
122 virtual bool Rewrite(const char* pszSkip=nullptr) = 0;
123
124 /** Back up the entire database to a file.
125 */
126 virtual bool Backup(const std::string& strDest) const = 0;
127
128 /** Make sure all changes are flushed to database file.
129 */
130 virtual void Flush() = 0;
131 /** Flush to the database file and close the database.
132 * Also close the environment if no other databases are open in it.
133 */
134 virtual void Close() = 0;
135 /* flush the wallet passively (TRY_LOCK)
136 ideal to be called periodically */
137 virtual bool PeriodicFlush() = 0;
138
139 virtual void IncrementUpdateCounter() = 0;
140
141 virtual void ReloadDbEnv() = 0;
142
143 /** Return path to main database file for logs and error messages. */
144 virtual std::string Filename() = 0;
145
146 virtual std::string Format() = 0;
147
148 std::atomic<unsigned int> nUpdateCounter;
149 unsigned int nLastSeen;
150 unsigned int nLastFlushed;
151 int64_t nLastWalletUpdate;
152
153 /** Make a DatabaseBatch connected to this database */
154 virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
155 };
156
157 /** RAII class that provides access to a DummyDatabase. Never fails. */
158 class DummyBatch : public DatabaseBatch
159 {
160 private:
161 bool ReadKey(CDataStream&& key, CDataStream& value) override { return true; }
162 bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite=true) override { return true; }
163 bool EraseKey(CDataStream&& key) override { return true; }
164 bool HasKey(CDataStream&& key) override { return true; }
165
166 public:
167 void Flush() override {}
168 void Close() override {}
169
170 bool StartCursor() override { return true; }
171 bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override { return true; }
172 void CloseCursor() override {}
173 bool TxnBegin() override { return true; }
174 bool TxnCommit() override { return true; }
175 bool TxnAbort() override { return true; }
176 };
177
178 /** A dummy WalletDatabase that does nothing and never fails. Only used by unit tests.
179 **/
180 class DummyDatabase : public WalletDatabase
181 {
182 public:
183 void Open() override {};
184 void AddRef() override {}
185 void RemoveRef() override {}
186 bool Rewrite(const char* pszSkip=nullptr) override { return true; }
187 bool Backup(const std::string& strDest) const override { return true; }
188 void Close() override {}
189 void Flush() override {}
190 bool PeriodicFlush() override { return true; }
191 void IncrementUpdateCounter() override { ++nUpdateCounter; }
192 void ReloadDbEnv() override {}
193 std::string Filename() override { return "dummy"; }
194 std::string Format() override { return "dummy"; }
195 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override { return std::make_unique<DummyBatch>(); }
196 };
197
198 enum class DatabaseFormat {
199 BERKELEY,
200 SQLITE,
201 };
202
203 struct DatabaseOptions {
204 bool require_existing = false;
205 bool require_create = false;
206 std::optional<DatabaseFormat> require_format;
207 uint64_t create_flags = 0;
208 SecureString create_passphrase;
209 bool verify = true;
210 };
211
212 enum class DatabaseStatus {
213 SUCCESS,
214 FAILED_BAD_PATH,
215 FAILED_BAD_FORMAT,
216 FAILED_ALREADY_LOADED,
217 FAILED_ALREADY_EXISTS,
218 FAILED_NOT_FOUND,
219 FAILED_CREATE,
220 FAILED_LOAD,
221 FAILED_VERIFY,
222 FAILED_ENCRYPT,
223 };
224
225 /** Recursively list database paths in directory. */
226 std::vector<fs::path> ListDatabases(const fs::path& path);
227
228 std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
229
230 fs::path BDBDataFile(const fs::path& path);
231 fs::path SQLiteDataFile(const fs::path& path);
232 bool IsBDBFile(const fs::path& path);
233 bool IsSQLiteFile(const fs::path& path);
234
235 #endif // BITCOIN_WALLET_DB_H
236