1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2021 The Limenka 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 LIMENKA_WALLET_DB_H
7 #define LIMENKA_WALLET_DB_H
8
9 #include <clientversion.h>
10 #include <streams.h>
11 #include <support/allocators/secure.h>
12 #include <util/fs.h>
13
14 #include <atomic>
15 #include <memory>
16 #include <optional>
17 #include <string>
18
19 class ArgsManager;
20 struct bilingual_str;
21
22 namespace wallet {
23 // BytePrefix compares equality with other byte spans that begin with the same prefix.
24 struct BytePrefix {
25 Span<const std::byte> prefix;
26 };
27 bool operator<(BytePrefix a, Span<const std::byte> b);
28 bool operator<(Span<const std::byte> a, BytePrefix b);
29
30 class DatabaseCursor
31 {
32 public:
33 explicit DatabaseCursor() = default;
34 virtual ~DatabaseCursor() = default;
35
36 DatabaseCursor(const DatabaseCursor&) = delete;
37 DatabaseCursor& operator=(const DatabaseCursor&) = delete;
38
39 enum class Status
40 {
41 FAIL,
42 MORE,
43 DONE,
44 };
45
46 virtual Status Next(DataStream& key, DataStream& value) { return Status::FAIL; }
47 };
48
49 /** RAII class that provides access to a WalletDatabase */
50 class DatabaseBatch
51 {
52 private:
53 virtual bool ReadKey(DataStream&& key, DataStream& value) = 0;
54 virtual bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) = 0;
55 virtual bool EraseKey(DataStream&& key) = 0;
56 virtual bool HasKey(DataStream&& key) = 0;
57
58 public:
59 explicit DatabaseBatch() = default;
60 virtual ~DatabaseBatch() = default;
61
62 DatabaseBatch(const DatabaseBatch&) = delete;
63 DatabaseBatch& operator=(const DatabaseBatch&) = delete;
64
65 virtual void Flush() = 0;
66 virtual void Close() = 0;
67
68 template <typename K, typename T>
69 bool Read(const K& key, T& value)
70 {
71 DataStream ssKey{};
72 ssKey.reserve(1000);
73 ssKey << key;
74
75 DataStream ssValue{};
76 if (!ReadKey(std::move(ssKey), ssValue)) return false;
77 try {
78 ssValue >> value;
79 return true;
80 } catch (const std::exception&) {
81 return false;
82 }
83 }
84
85 template <typename K, typename T>
86 bool Write(const K& key, const T& value, bool fOverwrite = true)
87 {
88 DataStream ssKey{};
89 ssKey.reserve(1000);
90 ssKey << key;
91
92 DataStream ssValue{};
93 ssValue.reserve(10000);
94 ssValue << value;
95
96 return WriteKey(std::move(ssKey), std::move(ssValue), fOverwrite);
97 }
98
99 template <typename K>
100 bool Erase(const K& key)
101 {
102 DataStream ssKey{};
103 ssKey.reserve(1000);
104 ssKey << key;
105
106 return EraseKey(std::move(ssKey));
107 }
108
109 template <typename K>
110 bool Exists(const K& key)
111 {
112 DataStream ssKey{};
113 ssKey.reserve(1000);
114 ssKey << key;
115
116 return HasKey(std::move(ssKey));
117 }
118 virtual bool ErasePrefix(Span<const std::byte> prefix) = 0;
119
120 virtual std::unique_ptr<DatabaseCursor> GetNewCursor() = 0;
121 virtual std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(Span<const std::byte> prefix) = 0;
122 virtual bool TxnBegin() = 0;
123 virtual bool TxnCommit() = 0;
124 virtual bool TxnAbort() = 0;
125 virtual bool HasActiveTxn() = 0;
126 };
127
128 /** An instance of this class represents one database.
129 **/
130 class WalletDatabase
131 {
132 public:
133 /** Create dummy DB handle */
134 WalletDatabase() : nUpdateCounter(0) {}
135 virtual ~WalletDatabase() = default;
136
137 /** Open the database if it is not already opened. */
138 virtual void Open() = 0;
139
140 //! Counts the number of active database users to be sure that the database is not closed while someone is using it
141 std::atomic<int> m_refcount{0};
142 /** Indicate the a new database user has began using the database. Increments m_refcount */
143 virtual void AddRef() = 0;
144 /** Indicate that database user has stopped using the database and that it could be flushed or closed. Decrement m_refcount */
145 virtual void RemoveRef() = 0;
146
147 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
148 */
149 virtual bool Rewrite(const char* pszSkip=nullptr) = 0;
150
151 /** Back up the entire database to a file.
152 */
153 virtual bool Backup(const std::string& strDest) const = 0;
154
155 /** Make sure all changes are flushed to database file.
156 */
157 virtual void Flush() = 0;
158 /** Flush to the database file and close the database.
159 * Also close the environment if no other databases are open in it.
160 */
161 virtual void Close() = 0;
162 /* flush the wallet passively (TRY_LOCK)
163 ideal to be called periodically */
164 virtual bool PeriodicFlush() = 0;
165
166 virtual void IncrementUpdateCounter() = 0;
167
168 virtual void ReloadDbEnv() = 0;
169
170 /** Return path to main database file for logs and error messages. */
171 virtual std::string Filename() = 0;
172
173 /** Return paths to all database created files */
174 virtual std::vector<fs::path> Files() = 0;
175
176 virtual std::string Format() = 0;
177
178 std::atomic<unsigned int> nUpdateCounter;
179 unsigned int nLastSeen{0};
180 unsigned int nLastFlushed{0};
181 int64_t nLastWalletUpdate{0};
182
183 /** Make a DatabaseBatch connected to this database */
184 virtual std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) = 0;
185 };
186
187 enum class DatabaseFormat {
188 BERKELEY,
189 SQLITE,
190 BERKELEY_RO,
191 BERKELEY_SWAP,
192 };
193
194 struct DatabaseOptions {
195 bool require_existing = false;
196 bool require_create = false;
197 std::optional<DatabaseFormat> require_format;
198 uint64_t create_flags = 0;
199 SecureString create_passphrase;
200
201 // Specialized options. Not every option is supported by every backend.
202 bool verify = true; //!< Check data integrity on load.
203 bool use_unsafe_sync = false; //!< Disable file sync for faster performance.
204 bool use_shared_memory = false; //!< Let other processes access the database.
205 int64_t max_log_mb = 100; //!< Max log size to allow before consolidating.
206 };
207
208 enum class DatabaseStatus {
209 SUCCESS,
210 FAILED_BAD_PATH,
211 FAILED_BAD_FORMAT,
212 FAILED_ALREADY_LOADED,
213 FAILED_ALREADY_EXISTS,
214 FAILED_NOT_FOUND,
215 FAILED_CREATE,
216 FAILED_LOAD,
217 FAILED_VERIFY,
218 FAILED_ENCRYPT,
219 FAILED_INVALID_BACKUP_FILE,
220 };
221
222 /** Recursively list database paths in directory. */
223 std::vector<std::pair<fs::path, std::string>> ListDatabases(const fs::path& path);
224
225 void ReadDatabaseArgs(const ArgsManager& args, DatabaseOptions& options);
226 std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
227
228 fs::path BDBDataFile(const fs::path& path);
229 fs::path SQLiteDataFile(const fs::path& path);
230 bool IsBDBFile(const fs::path& path);
231 bool IsSQLiteFile(const fs::path& path);
232 } // namespace wallet
233
234 #endif // LIMENKA_WALLET_DB_H
235