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_BDB_H
7 #define BITCOIN_WALLET_BDB_H
8 
9 #include <clientversion.h>
10 #include <fs.h>
11 #include <serialize.h>
12 #include <streams.h>
13 #include <util/system.h>
14 #include <wallet/db.h>
15 
16 #include <atomic>
17 #include <map>
18 #include <memory>
19 #include <string>
20 #include <unordered_map>
21 #include <vector>
22 
23 #if defined(__GNUC__) && !defined(__clang__)
24 #pragma GCC diagnostic push
25 #pragma GCC diagnostic ignored "-Wsuggest-override"
26 #endif
27 #include <db_cxx.h>
28 #if defined(__GNUC__) && !defined(__clang__)
29 #pragma GCC diagnostic pop
30 #endif
31 
32 struct bilingual_str;
33 
34 static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
35 static const bool DEFAULT_WALLET_PRIVDB = true;
36 
37 struct WalletDatabaseFileId {
38     u_int8_t value[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<std::string, 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 
59     explicit BerkeleyEnvironment(const fs::path& env_directory);
60     BerkeleyEnvironment();
61     ~BerkeleyEnvironment();
62     void Reset();
63 
IsMock()64     bool IsMock() const { return fMockDb; }
IsInitialized()65     bool IsInitialized() const { return fDbEnvInit; }
Directory()66     fs::path Directory() const { return strPath; }
67 
68     bool Open(bilingual_str& error);
69     void Close();
70     void Flush(bool fShutdown);
71     void CheckpointLSN(const std::string& strFile);
72 
73     void CloseDb(const std::string& strFile);
74     void ReloadDbEnv();
75 
76     DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
77     {
78         DbTxn* ptxn = nullptr;
79         int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
80         if (!ptxn || ret != 0)
81             return nullptr;
82         return ptxn;
83     }
84 };
85 
86 /** Get BerkeleyEnvironment given a directory path. */
87 std::shared_ptr<BerkeleyEnvironment> GetBerkeleyEnv(const fs::path& env_directory);
88 
89 class BerkeleyBatch;
90 
91 /** An instance of this class represents one database.
92  * For BerkeleyDB this is just a (env, strFile) tuple.
93  **/
94 class BerkeleyDatabase : public WalletDatabase
95 {
96 public:
97     BerkeleyDatabase() = delete;
98 
99     /** Create DB handle to real database */
BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env,std::string filename)100     BerkeleyDatabase(std::shared_ptr<BerkeleyEnvironment> env, std::string filename) :
101         WalletDatabase(), env(std::move(env)), strFile(std::move(filename))
102     {
103         auto inserted = this->env->m_databases.emplace(strFile, std::ref(*this));
104         assert(inserted.second);
105     }
106 
107     ~BerkeleyDatabase() override;
108 
109     /** Open the database if it is not already opened. */
110     void Open() override;
111 
112     /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
113      */
114     bool Rewrite(const char* pszSkip=nullptr) override;
115 
116     /** Indicate the a new database user has began using the database. */
117     void AddRef() override;
118     /** Indicate that database user has stopped using the database and that it could be flushed or closed. */
119     void RemoveRef() override;
120 
121     /** Back up the entire database to a file.
122      */
123     bool Backup(const std::string& strDest) const override;
124 
125     /** Make sure all changes are flushed to database file.
126      */
127     void Flush() override;
128     /** Flush to the database file and close the database.
129      *  Also close the environment if no other databases are open in it.
130      */
131     void Close() override;
132     /* flush the wallet passively (TRY_LOCK)
133        ideal to be called periodically */
134     bool PeriodicFlush() override;
135 
136     void IncrementUpdateCounter() override;
137 
138     void ReloadDbEnv() override;
139 
140     /** Verifies the environment and database file */
141     bool Verify(bilingual_str& error);
142 
143     /** Return path to main database filename */
Filename()144     std::string Filename() override { return (env->Directory() / strFile).string(); }
145 
Format()146     std::string Format() override { return "bdb"; }
147     /**
148      * Pointer to shared database environment.
149      *
150      * Normally there is only one BerkeleyDatabase object per
151      * BerkeleyEnvivonment, but in the special, backwards compatible case where
152      * multiple wallet BDB data files are loaded from the same directory, this
153      * will point to a shared instance that gets freed when the last data file
154      * is closed.
155      */
156     std::shared_ptr<BerkeleyEnvironment> env;
157 
158     /** Database pointer. This is initialized lazily and reset during flushes, so it can be null. */
159     std::unique_ptr<Db> m_db;
160 
161     std::string strFile;
162 
163     /** Make a BerkeleyBatch connected to this database */
164     std::unique_ptr<DatabaseBatch> MakeBatch(bool flush_on_close = true) override;
165 };
166 
167 /** RAII class that provides access to a Berkeley database */
168 class BerkeleyBatch : public DatabaseBatch
169 {
170     /** RAII class that automatically cleanses its data on destruction */
171     class SafeDbt final
172     {
173         Dbt m_dbt;
174 
175     public:
176         // construct Dbt with internally-managed data
177         SafeDbt();
178         // construct Dbt with provided data
179         SafeDbt(void* data, size_t size);
180         ~SafeDbt();
181 
182         // delegate to Dbt
183         const void* get_data() const;
184         u_int32_t get_size() const;
185 
186         // conversion operator to access the underlying Dbt
187         operator Dbt*();
188     };
189 
190 private:
191     bool ReadKey(CDataStream&& key, CDataStream& value) override;
192     bool WriteKey(CDataStream&& key, CDataStream&& value, bool overwrite = true) override;
193     bool EraseKey(CDataStream&& key) override;
194     bool HasKey(CDataStream&& key) override;
195 
196 protected:
197     Db* pdb;
198     std::string strFile;
199     DbTxn* activeTxn;
200     Dbc* m_cursor;
201     bool fReadOnly;
202     bool fFlushOnClose;
203     BerkeleyEnvironment *env;
204     BerkeleyDatabase& m_database;
205 
206 public:
207     explicit BerkeleyBatch(BerkeleyDatabase& database, const bool fReadOnly, bool fFlushOnCloseIn=true);
208     ~BerkeleyBatch() override;
209 
210     BerkeleyBatch(const BerkeleyBatch&) = delete;
211     BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
212 
213     void Flush() override;
214     void Close() override;
215 
216     bool StartCursor() override;
217     bool ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& complete) override;
218     void CloseCursor() override;
219     bool TxnBegin() override;
220     bool TxnCommit() override;
221     bool TxnAbort() override;
222 };
223 
224 std::string BerkeleyDatabaseVersion();
225 
226 /** Perform sanity check of runtime BDB version versus linked BDB version.
227  */
228 bool BerkeleyDatabaseSanityCheck();
229 
230 //! Return object giving access to Berkeley database at specified path.
231 std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
232 
233 #endif // BITCOIN_WALLET_BDB_H
234