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