1 // Copyright (c) 2012-2019 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_DBWRAPPER_H
6 #define BITCOIN_DBWRAPPER_H
7 
8 #include <clientversion.h>
9 #include <fs.h>
10 #include <serialize.h>
11 #include <streams.h>
12 #include <util/system.h>
13 #include <util/strencodings.h>
14 
15 #include <leveldb/db.h>
16 #include <leveldb/write_batch.h>
17 
18 static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
19 static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
20 
21 class dbwrapper_error : public std::runtime_error
22 {
23 public:
dbwrapper_error(const std::string & msg)24     explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
25 };
26 
27 class CDBWrapper;
28 
29 /** These should be considered an implementation detail of the specific database.
30  */
31 namespace dbwrapper_private {
32 
33 /** Handle database error by throwing dbwrapper_error exception.
34  */
35 void HandleError(const leveldb::Status& status);
36 
37 /** Work around circular dependency, as well as for testing in dbwrapper_tests.
38  * Database obfuscation should be considered an implementation detail of the
39  * specific database.
40  */
41 const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
42 
43 };
44 
45 /** Batch of changes queued to be written to a CDBWrapper */
46 class CDBBatch
47 {
48     friend class CDBWrapper;
49 
50 private:
51     const CDBWrapper &parent;
52     leveldb::WriteBatch batch;
53 
54     CDataStream ssKey;
55     CDataStream ssValue;
56 
57     size_t size_estimate;
58 
59 public:
60     /**
61      * @param[in] _parent   CDBWrapper that this batch is to be submitted to
62      */
CDBBatch(const CDBWrapper & _parent)63     explicit CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { };
64 
Clear()65     void Clear()
66     {
67         batch.Clear();
68         size_estimate = 0;
69     }
70 
71     template <typename K, typename V>
Write(const K & key,const V & value)72     void Write(const K& key, const V& value)
73     {
74         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
75         ssKey << key;
76         leveldb::Slice slKey(ssKey.data(), ssKey.size());
77 
78         ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
79         ssValue << value;
80         ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
81         leveldb::Slice slValue(ssValue.data(), ssValue.size());
82 
83         batch.Put(slKey, slValue);
84         // LevelDB serializes writes as:
85         // - byte: header
86         // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
87         // - byte[]: key
88         // - varint: value length
89         // - byte[]: value
90         // The formula below assumes the key and value are both less than 16k.
91         size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
92         ssKey.clear();
93         ssValue.clear();
94     }
95 
96     /* Write an empty value.  This is used for the expire-index
97        in the name database.  */
98     template <typename K>
Write(const K & key)99     void Write(const K& key)
100     {
101         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
102         ssKey << key;
103         leveldb::Slice slKey(ssKey.data(), ssKey.size());
104 
105         char dummy;
106         leveldb::Slice slValue(&dummy, 0);
107 
108         batch.Put(slKey, slValue);
109         ssKey.clear();
110     }
111 
112     template <typename K>
Erase(const K & key)113     void Erase(const K& key)
114     {
115         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
116         ssKey << key;
117         leveldb::Slice slKey(ssKey.data(), ssKey.size());
118 
119         batch.Delete(slKey);
120         // LevelDB serializes erases as:
121         // - byte: header
122         // - varint: key length
123         // - byte[]: key
124         // The formula below assumes the key is less than 16kB.
125         size_estimate += 2 + (slKey.size() > 127) + slKey.size();
126         ssKey.clear();
127     }
128 
SizeEstimate()129     size_t SizeEstimate() const { return size_estimate; }
130 };
131 
132 class CDBIterator
133 {
134 private:
135     const CDBWrapper &parent;
136     leveldb::Iterator *piter;
137 
138 public:
139 
140     /**
141      * @param[in] _parent          Parent CDBWrapper instance.
142      * @param[in] _piter           The original leveldb iterator.
143      */
CDBIterator(const CDBWrapper & _parent,leveldb::Iterator * _piter)144     CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
145         parent(_parent), piter(_piter) { };
146     ~CDBIterator();
147 
148     bool Valid() const;
149 
150     void SeekToFirst();
151 
Seek(const K & key)152     template<typename K> void Seek(const K& key) {
153         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
154         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
155         ssKey << key;
156         leveldb::Slice slKey(ssKey.data(), ssKey.size());
157         piter->Seek(slKey);
158     }
159 
160     void Next();
161 
GetKey(K & key)162     template<typename K> bool GetKey(K& key) {
163         leveldb::Slice slKey = piter->key();
164         try {
165             CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
166             ssKey >> key;
167         } catch (const std::exception&) {
168             return false;
169         }
170         return true;
171     }
172 
GetValue(V & value)173     template<typename V> bool GetValue(V& value) {
174         leveldb::Slice slValue = piter->value();
175         try {
176             CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
177             ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
178             ssValue >> value;
179         } catch (const std::exception&) {
180             return false;
181         }
182         return true;
183     }
184 
GetValueSize()185     unsigned int GetValueSize() {
186         return piter->value().size();
187     }
188 
189 };
190 
191 class CDBWrapper
192 {
193     friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
194 private:
195     //! custom environment this database is using (may be nullptr in case of default environment)
196     leveldb::Env* penv;
197 
198     //! database options used
199     leveldb::Options options;
200 
201     //! options used when reading from the database
202     leveldb::ReadOptions readoptions;
203 
204     //! options used when iterating over values of the database
205     leveldb::ReadOptions iteroptions;
206 
207     //! options used when writing to the database
208     leveldb::WriteOptions writeoptions;
209 
210     //! options used when sync writing to the database
211     leveldb::WriteOptions syncoptions;
212 
213     //! the database itself
214     leveldb::DB* pdb;
215 
216     //! the name of this database
217     std::string m_name;
218 
219     //! a key used for optional XOR-obfuscation of the database
220     std::vector<unsigned char> obfuscate_key;
221 
222     //! the key under which the obfuscation key is stored
223     static const std::string OBFUSCATE_KEY_KEY;
224 
225     //! the length of the obfuscate key in number of bytes
226     static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
227 
228     std::vector<unsigned char> CreateObfuscateKey() const;
229 
230 public:
231     /**
232      * @param[in] path        Location in the filesystem where leveldb data will be stored.
233      * @param[in] nCacheSize  Configures various leveldb cache settings.
234      * @param[in] fMemory     If true, use leveldb's memory environment.
235      * @param[in] fWipe       If true, remove all existing data.
236      * @param[in] obfuscate   If true, store data obfuscated via simple XOR. If false, XOR
237      *                        with a zero'd byte array.
238      */
239     CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
240     ~CDBWrapper();
241 
242     CDBWrapper(const CDBWrapper&) = delete;
243     CDBWrapper& operator=(const CDBWrapper&) = delete;
244 
245     template <typename K, typename V>
Read(const K & key,V & value)246     bool Read(const K& key, V& value) const
247     {
248         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
249         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
250         ssKey << key;
251         leveldb::Slice slKey(ssKey.data(), ssKey.size());
252 
253         std::string strValue;
254         leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
255         if (!status.ok()) {
256             if (status.IsNotFound())
257                 return false;
258             LogPrintf("LevelDB read failure: %s\n", status.ToString());
259             dbwrapper_private::HandleError(status);
260         }
261         try {
262             CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
263             ssValue.Xor(obfuscate_key);
264             ssValue >> value;
265         } catch (const std::exception&) {
266             return false;
267         }
268         return true;
269     }
270 
271     template <typename K, typename V>
272     bool Write(const K& key, const V& value, bool fSync = false)
273     {
274         CDBBatch batch(*this);
275         batch.Write(key, value);
276         return WriteBatch(batch, fSync);
277     }
278 
279     template <typename K>
Exists(const K & key)280     bool Exists(const K& key) const
281     {
282         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
283         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
284         ssKey << key;
285         leveldb::Slice slKey(ssKey.data(), ssKey.size());
286 
287         std::string strValue;
288         leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
289         if (!status.ok()) {
290             if (status.IsNotFound())
291                 return false;
292             LogPrintf("LevelDB read failure: %s\n", status.ToString());
293             dbwrapper_private::HandleError(status);
294         }
295         return true;
296     }
297 
298     template <typename K>
299     bool Erase(const K& key, bool fSync = false)
300     {
301         CDBBatch batch(*this);
302         batch.Erase(key);
303         return WriteBatch(batch, fSync);
304     }
305 
306     bool WriteBatch(CDBBatch& batch, bool fSync = false);
307 
308     // Get an estimate of LevelDB memory usage (in bytes).
309     size_t DynamicMemoryUsage() const;
310 
NewIterator()311     CDBIterator *NewIterator()
312     {
313         return new CDBIterator(*this, pdb->NewIterator(iteroptions));
314     }
315 
316     /**
317      * Return true if the database managed by this class contains no entries.
318      */
319     bool IsEmpty();
320 
321     template<typename K>
EstimateSize(const K & key_begin,const K & key_end)322     size_t EstimateSize(const K& key_begin, const K& key_end) const
323     {
324         CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
325         ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
326         ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
327         ssKey1 << key_begin;
328         ssKey2 << key_end;
329         leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
330         leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
331         uint64_t size = 0;
332         leveldb::Range range(slKey1, slKey2);
333         pdb->GetApproximateSizes(&range, 1, &size);
334         return size;
335     }
336 
337     /**
338      * Compact a certain range of keys in the database.
339      */
340     template<typename K>
CompactRange(const K & key_begin,const K & key_end)341     void CompactRange(const K& key_begin, const K& key_end) const
342     {
343         CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
344         ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
345         ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
346         ssKey1 << key_begin;
347         ssKey2 << key_end;
348         leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
349         leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
350         pdb->CompactRange(&slKey1, &slKey2);
351     }
352 
353 };
354 
355 #endif // BITCOIN_DBWRAPPER_H
356