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