Line data Source code
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 0 : class dbwrapper_error : public std::runtime_error
22 : {
23 : public:
24 0 : 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 17774 : 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 : */
63 17774 : explicit CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { };
64 :
65 0 : void Clear()
66 : {
67 0 : batch.Clear();
68 0 : size_estimate = 0;
69 0 : }
70 :
71 : template <typename K, typename V>
72 194929 : void Write(const K& key, const V& value)
73 : {
74 194929 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
75 194929 : ssKey << key;
76 194929 : leveldb::Slice slKey(ssKey.data(), ssKey.size());
77 :
78 194929 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
79 194929 : ssValue << value;
80 194929 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
81 194929 : leveldb::Slice slValue(ssValue.data(), ssValue.size());
82 :
83 194929 : 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 194929 : size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
92 194929 : ssKey.clear();
93 194929 : ssValue.clear();
94 194929 : }
95 :
96 : template <typename K>
97 23487 : void Erase(const K& key)
98 : {
99 23487 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
100 23487 : ssKey << key;
101 23487 : leveldb::Slice slKey(ssKey.data(), ssKey.size());
102 :
103 23487 : batch.Delete(slKey);
104 : // LevelDB serializes erases as:
105 : // - byte: header
106 : // - varint: key length
107 : // - byte[]: key
108 : // The formula below assumes the key is less than 16kB.
109 23487 : size_estimate += 2 + (slKey.size() > 127) + slKey.size();
110 23487 : ssKey.clear();
111 23487 : }
112 :
113 232215 : size_t SizeEstimate() const { return size_estimate; }
114 : };
115 :
116 : class CDBIterator
117 : {
118 : private:
119 : const CDBWrapper &parent;
120 : leveldb::Iterator *piter;
121 :
122 : public:
123 :
124 : /**
125 : * @param[in] _parent Parent CDBWrapper instance.
126 : * @param[in] _piter The original leveldb iterator.
127 : */
128 3574 : CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
129 3574 : parent(_parent), piter(_piter) { };
130 : ~CDBIterator();
131 :
132 : bool Valid() const;
133 :
134 : void SeekToFirst();
135 :
136 1478 : template<typename K> void Seek(const K& key) {
137 1478 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
138 1478 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
139 1478 : ssKey << key;
140 1478 : leveldb::Slice slKey(ssKey.data(), ssKey.size());
141 1478 : piter->Seek(slKey);
142 1478 : }
143 :
144 : void Next();
145 :
146 63383 : template<typename K> bool GetKey(K& key) {
147 63383 : leveldb::Slice slKey = piter->key();
148 : try {
149 63383 : CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
150 63383 : ssKey >> key;
151 63383 : } catch (const std::exception&) {
152 : return false;
153 307 : }
154 63076 : return true;
155 63690 : }
156 :
157 62886 : template<typename V> bool GetValue(V& value) {
158 62886 : leveldb::Slice slValue = piter->value();
159 : try {
160 62886 : CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
161 62886 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
162 62886 : ssValue >> value;
163 62886 : } catch (const std::exception&) {
164 : return false;
165 0 : }
166 62886 : return true;
167 62886 : }
168 :
169 0 : unsigned int GetValueSize() {
170 0 : return piter->value().size();
171 : }
172 :
173 : };
174 :
175 : class CDBWrapper
176 : {
177 : friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
178 : private:
179 : //! custom environment this database is using (may be nullptr in case of default environment)
180 : leveldb::Env* penv;
181 :
182 : //! database options used
183 : leveldb::Options options;
184 :
185 : //! options used when reading from the database
186 : leveldb::ReadOptions readoptions;
187 :
188 : //! options used when iterating over values of the database
189 : leveldb::ReadOptions iteroptions;
190 :
191 : //! options used when writing to the database
192 : leveldb::WriteOptions writeoptions;
193 :
194 : //! options used when sync writing to the database
195 : leveldb::WriteOptions syncoptions;
196 :
197 : //! the database itself
198 : leveldb::DB* pdb;
199 :
200 : //! the name of this database
201 : std::string m_name;
202 :
203 : //! a key used for optional XOR-obfuscation of the database
204 : std::vector<unsigned char> obfuscate_key;
205 :
206 : //! the key under which the obfuscation key is stored
207 : static const std::string OBFUSCATE_KEY_KEY;
208 :
209 : //! the length of the obfuscate key in number of bytes
210 : static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
211 :
212 : std::vector<unsigned char> CreateObfuscateKey() const;
213 :
214 : public:
215 : /**
216 : * @param[in] path Location in the filesystem where leveldb data will be stored.
217 : * @param[in] nCacheSize Configures various leveldb cache settings.
218 : * @param[in] fMemory If true, use leveldb's memory environment.
219 : * @param[in] fWipe If true, remove all existing data.
220 : * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
221 : * with a zero'd byte array.
222 : */
223 : CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
224 : ~CDBWrapper();
225 :
226 : CDBWrapper(const CDBWrapper&) = delete;
227 : CDBWrapper& operator=(const CDBWrapper&) = delete;
228 :
229 : template <typename K, typename V>
230 5806686 : bool Read(const K& key, V& value) const
231 : {
232 5806686 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
233 5806686 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
234 5806686 : ssKey << key;
235 5806686 : leveldb::Slice slKey(ssKey.data(), ssKey.size());
236 :
237 5806686 : std::string strValue;
238 5806686 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
239 5806686 : if (!status.ok()) {
240 5712359 : if (status.IsNotFound())
241 5712359 : return false;
242 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
243 0 : dbwrapper_private::HandleError(status);
244 : }
245 : try {
246 94327 : CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
247 94327 : ssValue.Xor(obfuscate_key);
248 94327 : ssValue >> value;
249 94327 : } catch (const std::exception&) {
250 : return false;
251 0 : }
252 94327 : return true;
253 5806686 : }
254 :
255 : template <typename K, typename V>
256 5021 : bool Write(const K& key, const V& value, bool fSync = false)
257 : {
258 5021 : CDBBatch batch(*this);
259 5021 : batch.Write(key, value);
260 5021 : return WriteBatch(batch, fSync);
261 5021 : }
262 :
263 : template <typename K>
264 495 : bool Exists(const K& key) const
265 : {
266 495 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
267 495 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
268 495 : ssKey << key;
269 495 : leveldb::Slice slKey(ssKey.data(), ssKey.size());
270 :
271 495 : std::string strValue;
272 495 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
273 495 : if (!status.ok()) {
274 495 : if (status.IsNotFound())
275 495 : return false;
276 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
277 0 : dbwrapper_private::HandleError(status);
278 : }
279 0 : return true;
280 495 : }
281 :
282 : template <typename K>
283 8 : bool Erase(const K& key, bool fSync = false)
284 : {
285 8 : CDBBatch batch(*this);
286 8 : batch.Erase(key);
287 8 : return WriteBatch(batch, fSync);
288 8 : }
289 :
290 : bool WriteBatch(CDBBatch& batch, bool fSync = false);
291 :
292 : // Get an estimate of LevelDB memory usage (in bytes).
293 : size_t DynamicMemoryUsage() const;
294 :
295 1787 : CDBIterator *NewIterator()
296 : {
297 1787 : return new CDBIterator(*this, pdb->NewIterator(iteroptions));
298 0 : }
299 :
300 : /**
301 : * Return true if the database managed by this class contains no entries.
302 : */
303 : bool IsEmpty();
304 :
305 : template<typename K>
306 7 : size_t EstimateSize(const K& key_begin, const K& key_end) const
307 : {
308 7 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
309 7 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
310 7 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
311 7 : ssKey1 << key_begin;
312 7 : ssKey2 << key_end;
313 7 : leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
314 7 : leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
315 7 : uint64_t size = 0;
316 7 : leveldb::Range range(slKey1, slKey2);
317 7 : pdb->GetApproximateSizes(&range, 1, &size);
318 7 : return size;
319 7 : }
320 :
321 : /**
322 : * Compact a certain range of keys in the database.
323 : */
324 : template<typename K>
325 0 : void CompactRange(const K& key_begin, const K& key_end) const
326 : {
327 0 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
328 0 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
329 0 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
330 0 : ssKey1 << key_begin;
331 0 : ssKey2 << key_end;
332 0 : leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
333 0 : leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
334 0 : pdb->CompactRange(&slKey1, &slKey2);
335 0 : }
336 :
337 : };
338 :
339 : #endif // BITCOIN_DBWRAPPER_H
|