1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2022 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_BDB_H
7 #define LIMENKA_WALLET_BDB_H
8
9 #include <clientversion.h>
10 #include <common/system.h>
11 #include <serialize.h>
12 #include <streams.h>
13 #include <util/fs.h>
14 #include <wallet/db.h>
15
16 #include <atomic>
17 #include <condition_variable>
18 #include <map>
19 #include <memory>
20 #include <string>
21 #include <unordered_map>
22 #include <vector>
23
24 struct bilingual_str;
25
26 class DbEnv;
27 class DbTxn;
28 class Db;
29 class Dbc;
30
31 // This constant was introduced in BDB 4.0.14 and has never changed, but there
32 // is a belt-and-suspenders check in the cpp file just in case.
33 #define BDB_DB_FILE_ID_LEN 20 /* Unique file ID length. */
34
35 namespace wallet {
36
37 struct WalletDatabaseFileId {
38 uint8_t value[BDB_DB_FILE_ID_LEN];
39 bool operator==(const WalletDatabaseFileId& rhs) const;
40 };
41
42 class BerkeleyDatabase;
43
44 class BerkeleyEnvironment
45 {
46 private:
47 bool fDbEnvInit;
48 bool fMockDb;
49 // Don't change into fs::path, as that can result in
50 // shutdown problems/crashes caused by a static initialized internal pointer.
51 std::string strPath;
52
53 public:
54 std::unique_ptr<DbEnv> dbenv;
55 std::map<fs::path, std::reference_wrapper<BerkeleyDatabase>> m_databases;
56 std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
57 std::condition_variable_any m_db_in_use;
58 bool m_use_shared_memory;
59
60 explicit BerkeleyEnvironment(const fs::path& env_directory, bool use_shared_memory);
61 BerkeleyEnvironment();
62 ~BerkeleyEnvironment();
63 void Reset();
64
65 bool IsMock() const { return fMockDb; }
66 bool IsInitialized() const { return fDbEnvInit; }
67 fs::path Directory() const { return fs::PathFromString(strPath); }
68
69 bool Open(bilingual_str& error);
70 void Close();
71 void Flush(bool fShutdown);
72 [[nodiscard]] bool CheckpointLSN(const std::string& strFile);
73
74 void CloseDb(const fs::path& filename);
75 void ReloadDbEnv();
76
77 DbTxn* TxnBegin(int flags);
78 };
79
80 /** Get BerkeleyEnvironment given a directory path. */
81 std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory, bool use_shared_memory);
82
83 class BerkeleyBatch;
84
85 /** An instance of this class represents one database.
86 * For BerkeleyDB this is just a (env, strFile) tuple.
87 **/
88 class BerkeleyDatabase : public WalletDatabase
89 {
90 public:
91 BerkeleyDatabase() = delete;
92
93 /** Create DB handle to real database */
94 BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, fs::path filename, const DatabaseOptions& options);
95
96 ~BerkeleyDatabase() override;
97
98 /** Open the database if it is not already opened. */
99 void Open() override;
100
101 /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
102 */
103 bool Rewrite(const char* pszSkip=nullptr) override;
104
105 /** Indicate that a new database user has begun using the database. */
106 void AddRef() override;
107 /** Indicate that database user has stopped using the database and that it could be flushed or closed. */
108 void RemoveRef() override;
109
110 /** Back up the entire database to a file.
111 */
112 bool Backup(const std::string& strDest) const override;
113
114 /** Make sure all changes are flushed to database file.
115 */
116 void Flush() override;
117 /** Flush to the database file and close the database.
118 * Also close the environment if no other databases are open in it.
119 */
120 void Close() override;
121 /* flush the wallet passively (TRY_LOCK)
122 ideal to be called periodically */
123 bool PeriodicFlush() override;
124
125 void IncrementUpdateCounter() override;
126
127 void ReloadDbEnv() override;
128
129 /** Verifies the environment and database file */
130 bool Verify(bilingual_str& error);
131
132 /** Return path to main database filename */
133 std::string Filename() override { return fs::PathToString(env->Directory() / m_filename); }
134
135 std::vector<fs::path> Files() override;
136
137 std::string Format() override { return "bdb"; }
138 /**
139 * Pointer to shared database environment.
140 *
141 * Normally there is only one BerkeleyDatabase object per
142 * BerkeleyEnvivonment, but in the special, backwards compatible case where
143 * multiple wallet BDB data files are loaded from the same directory, this
144 * will point to a shared instance that gets freed when the last data file
145 * is closed.
146 */
147 std::shared_ptr<BerkeleyEnvironment> env;
148
149 /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
150 std::unique_ptr<Db> m_db;
151
152 // Whether to byteswap
153 bool m_byteswap;
154
155 fs::path m_filename;
156 int64_t m_max_log_mb;
157
158 /** Make a BerkeleyBatch connected to this database */
159 std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
160 };
161
162 class BerkeleyCursor : public DatabaseCursor
163 {
164 private:
165 Dbc* m_cursor;
166 std::vector<std::byte> m_key_prefix;
167 bool m_first{true};
168
169 public:
170 // Constructor for cursor for records matching the prefix
171 // To match all records, an empty prefix may be provided.
172 explicit BerkeleyCursor(BerkeleyDatabase& database, const BerkeleyBatch& batch, Span<const std::byte> prefix = {});
173 ~BerkeleyCursor() override;
174
175 Status Next(DataStream& key, DataStream& value) override;
176 Dbc* dbc() const { return m_cursor; }
177 };
178
179 /** RAII class that provides access to a Berkeley database */
180 class BerkeleyBatch : public DatabaseBatch
181 {
182 private:
183 bool ReadKey(DataStream&& key, DataStream& value) override;
184 bool WriteKey(DataStream&& key, DataStream&& value, bool overwrite = true) override;
185 bool EraseKey(DataStream&& key) override;
186 bool HasKey(DataStream&& key) override;
187 bool ErasePrefix(Span<const std::byte> prefix) override;
188
189 protected:
190 Db* pdb{nullptr};
191 std::string strFile;
192 DbTxn* activeTxn{nullptr};
193 bool fReadOnly;
194 bool fFlushOnClose;
195 BerkeleyEnvironment *env;
196 BerkeleyDatabase& m_database;
197
198 public:
199 explicit BerkeleyBatch(BerkeleyDatabase& database, const bool fReadOnly, bool fFlushOnCloseIn=true);
200 ~BerkeleyBatch() override;
201
202 BerkeleyBatch(const BerkeleyBatch&) = delete;
203 BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
204
205 void Flush() override;
206 void Close() override;
207
208 std::unique_ptr<DatabaseCursor> GetNewCursor() override;
209 std::unique_ptr<DatabaseCursor> GetNewPrefixCursor(Span<const std::byte> prefix) override;
210 bool TxnBegin() override;
211 bool TxnCommit() override;
212 bool TxnAbort() override;
213 bool HasActiveTxn() override { return activeTxn != nullptr; }
214 DbTxn* txn() const { return activeTxn; }
215 };
216
217 std::string BerkeleyDatabaseVersion();
218
219 /** Perform sanity check of runtime BDB version versus linked BDB version.
220 */
221 bool BerkeleyDatabaseSanityCheck();
222
223 //! Return object giving access to Berkeley database at specified path.
224 std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
225 } // namespace wallet
226
227 #endif // LIMENKA_WALLET_BDB_H
228