Bitcoin ABC  0.29.6
P2P Digital Currency
dbwrapper.h
Go to the documentation of this file.
1 // Copyright (c) 2012-2016 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 <common/system.h>
10 #include <logging.h>
11 #include <serialize.h>
12 #include <span.h>
13 #include <streams.h>
14 #include <util/fs.h>
15 #include <util/strencodings.h>
16 
17 #include <leveldb/db.h>
18 #include <leveldb/write_batch.h>
19 
20 #include <optional>
21 
22 static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
23 static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
24 
26 struct DBOptions {
28  bool force_compact = false;
29 };
30 
32 struct DBParams {
36  size_t cache_bytes;
38  bool memory_only = false;
40  bool wipe_data = false;
43  bool obfuscate = false;
46 };
47 
48 class dbwrapper_error : public std::runtime_error {
49 public:
50  explicit dbwrapper_error(const std::string &msg)
51  : std::runtime_error(msg) {}
52 };
53 
54 class CDBWrapper;
55 
56 namespace dbwrapper {
57 using leveldb::DestroyDB;
58 }
62 namespace dbwrapper_private {
63 
67 void HandleError(const leveldb::Status &status);
68 
74 const std::vector<uint8_t> &GetObfuscateKey(const CDBWrapper &w);
75 }; // namespace dbwrapper_private
76 
78 class CDBBatch {
79  friend class CDBWrapper;
80 
81 private:
83  leveldb::WriteBatch batch;
84 
87 
88  size_t size_estimate;
89 
90 public:
94  explicit CDBBatch(const CDBWrapper &_parent)
95  : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION),
97 
98  void Clear() {
99  batch.Clear();
100  size_estimate = 0;
101  }
102 
103  template <typename K, typename V> void Write(const K &key, const V &value) {
105  ssKey << key;
106  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
107 
109  ssValue << value;
111  leveldb::Slice slValue((const char *)ssValue.data(), ssValue.size());
112 
113  batch.Put(slKey, slValue);
114  // LevelDB serializes writes as:
115  // - byte: header
116  // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
117  // - byte[]: key
118  // - varint: value length
119  // - byte[]: value
120  // The formula below assumes the key and value are both less than 16k.
121  size_estimate += 3 + (slKey.size() > 127) + slKey.size() +
122  (slValue.size() > 127) + slValue.size();
123  ssKey.clear();
124  ssValue.clear();
125  }
126 
127  template <typename K> void Erase(const K &key) {
129  ssKey << key;
130  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
131 
132  batch.Delete(slKey);
133  // LevelDB serializes erases as:
134  // - byte: header
135  // - varint: key length
136  // - byte[]: key
137  // The formula below assumes the key is less than 16kB.
138  size_estimate += 2 + (slKey.size() > 127) + slKey.size();
139  ssKey.clear();
140  }
141 
142  size_t SizeEstimate() const { return size_estimate; }
143 };
144 
145 class CDBIterator {
146 private:
148  leveldb::Iterator *piter;
149 
150 public:
155  CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter)
156  : parent(_parent), piter(_piter){};
157  ~CDBIterator();
158 
159  bool Valid() const;
160 
161  void SeekToFirst();
162 
163  template <typename K> void Seek(const K &key) {
166  ssKey << key;
167  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
168  piter->Seek(slKey);
169  }
170 
171  void Next();
172 
173  template <typename K> bool GetKey(K &key) {
174  leveldb::Slice slKey = piter->key();
175  try {
177  ssKey >> key;
178  } catch (const std::exception &) {
179  return false;
180  }
181  return true;
182  }
183 
184  template <typename V> bool GetValue(V &value) {
185  leveldb::Slice slValue = piter->value();
186  try {
187  CDataStream ssValue{MakeByteSpan(slValue), SER_DISK,
190  ssValue >> value;
191  } catch (const std::exception &) {
192  return false;
193  }
194  return true;
195  }
196 
197  unsigned int GetValueSize() { return piter->value().size(); }
198 };
199 
200 class CDBWrapper {
201  friend const std::vector<uint8_t> &
203 
204 private:
207  leveldb::Env *penv;
208 
210  leveldb::Options options;
211 
213  leveldb::ReadOptions readoptions;
214 
216  leveldb::ReadOptions iteroptions;
217 
219  leveldb::WriteOptions writeoptions;
220 
222  leveldb::WriteOptions syncoptions;
223 
225  leveldb::DB *pdb;
226 
228  std::string m_name;
229 
231  std::vector<uint8_t> obfuscate_key;
232 
234  static const std::string OBFUSCATE_KEY_KEY;
235 
237  static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
238 
239  std::vector<uint8_t> CreateObfuscateKey() const;
240 
243 
246 
247 public:
248  CDBWrapper(const DBParams &params);
249  ~CDBWrapper();
250 
251  CDBWrapper(const CDBWrapper &) = delete;
252  CDBWrapper &operator=(const CDBWrapper &) = delete;
253 
254  template <typename K, typename V> bool Read(const K &key, V &value) const {
257  ssKey << key;
258  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
259 
260  std::string strValue;
261  leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
262  if (!status.ok()) {
263  if (status.IsNotFound()) return false;
264  LogPrintf("LevelDB read failure: %s\n", status.ToString());
266  }
267  try {
268  CDataStream ssValue{MakeByteSpan(strValue), SER_DISK,
270  ssValue.Xor(obfuscate_key);
271  ssValue >> value;
272  } catch (const std::exception &) {
273  return false;
274  }
275  return true;
276  }
277 
278  template <typename K, typename V>
279  bool Write(const K &key, const V &value, bool fSync = false) {
280  CDBBatch batch(*this);
281  batch.Write(key, value);
282  return WriteBatch(batch, fSync);
283  }
284 
286  std::optional<fs::path> StoragePath() {
287  if (m_is_memory) {
288  return {};
289  }
290  return m_path;
291  }
292 
293  template <typename K> bool Exists(const K &key) const {
296  ssKey << key;
297  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
298 
299  std::string strValue;
300  leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
301  if (!status.ok()) {
302  if (status.IsNotFound()) return false;
303  LogPrintf("LevelDB read failure: %s\n", status.ToString());
305  }
306  return true;
307  }
308 
309  template <typename K> bool Erase(const K &key, bool fSync = false) {
310  CDBBatch batch(*this);
311  batch.Erase(key);
312  return WriteBatch(batch, fSync);
313  }
314 
315  bool WriteBatch(CDBBatch &batch, bool fSync = false);
316 
317  // Get an estimate of LevelDB memory usage (in bytes).
318  size_t DynamicMemoryUsage() const;
319 
321  return new CDBIterator(*this, pdb->NewIterator(iteroptions));
322  }
323 
327  bool IsEmpty();
328 
329  template <typename K>
330  size_t EstimateSize(const K &key_begin, const K &key_end) const {
332  ssKey2(SER_DISK, CLIENT_VERSION);
333  ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
335  ssKey1 << key_begin;
336  ssKey2 << key_end;
337  leveldb::Slice slKey1((const char *)ssKey1.data(), ssKey1.size());
338  leveldb::Slice slKey2((const char *)ssKey2.data(), ssKey2.size());
339  uint64_t size = 0;
340  leveldb::Range range(slKey1, slKey2);
341  pdb->GetApproximateSizes(&range, 1, &size);
342  return size;
343  }
344 
348  template <typename K>
349  void CompactRange(const K &key_begin, const K &key_end) const {
351  ssKey2(SER_DISK, CLIENT_VERSION);
352  ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
354  ssKey1 << key_begin;
355  ssKey2 << key_end;
356  leveldb::Slice slKey1((const char *)ssKey1.data(), ssKey1.size());
357  leveldb::Slice slKey2((const char *)ssKey2.data(), ssKey2.size());
358  pdb->CompactRange(&slKey1, &slKey2);
359  }
360 };
361 
362 #endif // BITCOIN_DBWRAPPER_H
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:78
void Erase(const K &key)
Definition: dbwrapper.h:127
size_t SizeEstimate() const
Definition: dbwrapper.h:142
CDataStream ssKey
Definition: dbwrapper.h:85
CDataStream ssValue
Definition: dbwrapper.h:86
size_t size_estimate
Definition: dbwrapper.h:88
void Write(const K &key, const V &value)
Definition: dbwrapper.h:103
void Clear()
Definition: dbwrapper.h:98
CDBBatch(const CDBWrapper &_parent)
Definition: dbwrapper.h:94
leveldb::WriteBatch batch
Definition: dbwrapper.h:83
const CDBWrapper & parent
Definition: dbwrapper.h:82
bool GetValue(V &value)
Definition: dbwrapper.h:184
unsigned int GetValueSize()
Definition: dbwrapper.h:197
bool GetKey(K &key)
Definition: dbwrapper.h:173
leveldb::Iterator * piter
Definition: dbwrapper.h:148
void Seek(const K &key)
Definition: dbwrapper.h:163
const CDBWrapper & parent
Definition: dbwrapper.h:147
bool Valid() const
Definition: dbwrapper.cpp:251
void SeekToFirst()
Definition: dbwrapper.cpp:254
void Next()
Definition: dbwrapper.cpp:257
CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter)
Definition: dbwrapper.h:155
CDBWrapper(const CDBWrapper &)=delete
CDBIterator * NewIterator()
Definition: dbwrapper.h:320
size_t DynamicMemoryUsage() const
Definition: dbwrapper.cpp:214
leveldb::Env * penv
custom environment this database is using (may be nullptr in case of default environment)
Definition: dbwrapper.h:207
bool WriteBatch(CDBBatch &batch, bool fSync=false)
Definition: dbwrapper.cpp:195
bool Read(const K &key, V &value) const
Definition: dbwrapper.h:254
std::vector< uint8_t > CreateObfuscateKey() const
Returns a string (consisting of 8 random bytes) suitable for use as an obfuscating XOR key.
Definition: dbwrapper.cpp:236
std::string m_name
the name of this database
Definition: dbwrapper.h:228
bool Erase(const K &key, bool fSync=false)
Definition: dbwrapper.h:309
bool Write(const K &key, const V &value, bool fSync=false)
Definition: dbwrapper.h:279
bool Exists(const K &key) const
Definition: dbwrapper.h:293
std::vector< uint8_t > obfuscate_key
a key used for optional XOR-obfuscation of the database
Definition: dbwrapper.h:231
CDBWrapper & operator=(const CDBWrapper &)=delete
CDBWrapper(const DBParams &params)
Definition: dbwrapper.cpp:120
std::optional< fs::path > StoragePath()
Definition: dbwrapper.h:286
leveldb::Options options
database options used
Definition: dbwrapper.h:210
static const unsigned int OBFUSCATE_KEY_NUM_BYTES
the length of the obfuscate key in number of bytes
Definition: dbwrapper.h:237
static const std::string OBFUSCATE_KEY_KEY
the key under which the obfuscation key is stored
Definition: dbwrapper.h:234
leveldb::WriteOptions writeoptions
options used when writing to the database
Definition: dbwrapper.h:219
const fs::path m_path
path to filesystem storage
Definition: dbwrapper.h:242
leveldb::WriteOptions syncoptions
options used when sync writing to the database
Definition: dbwrapper.h:222
bool m_is_memory
whether or not the database resides in memory
Definition: dbwrapper.h:245
leveldb::DB * pdb
the database itself
Definition: dbwrapper.h:225
leveldb::ReadOptions iteroptions
options used when iterating over values of the database
Definition: dbwrapper.h:216
void CompactRange(const K &key_begin, const K &key_end) const
Compact a certain range of keys in the database.
Definition: dbwrapper.h:349
bool IsEmpty()
Return true if the database managed by this class contains no entries.
Definition: dbwrapper.cpp:242
leveldb::ReadOptions readoptions
options used when reading from the database
Definition: dbwrapper.h:213
size_t EstimateSize(const K &key_begin, const K &key_end) const
Definition: dbwrapper.h:330
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:177
value_type * data()
Definition: streams.h:243
void reserve(size_type n)
Definition: streams.h:228
void Xor(const std::vector< uint8_t > &key)
XOR the contents of this stream with a certain key.
Definition: streams.h:408
size_type size() const
Definition: streams.h:223
void clear()
Definition: streams.h:233
dbwrapper_error(const std::string &msg)
Definition: dbwrapper.h:50
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
static const size_t DBWRAPPER_PREALLOC_KEY_SIZE
Definition: dbwrapper.h:22
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE
Definition: dbwrapper.h:23
#define LogPrintf(...)
Definition: logging.h:207
These should be considered an implementation detail of the specific database.
Definition: dbwrapper.cpp:261
void HandleError(const leveldb::Status &status)
Handle database error by throwing dbwrapper_error exception.
Definition: dbwrapper.cpp:263
const std::vector< uint8_t > & GetObfuscateKey(const CDBWrapper &w)
Work around circular dependency, as well as for testing in dbwrapper_tests.
Definition: dbwrapper.cpp:274
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:257
@ SER_DISK
Definition: serialize.h:153
Span< const std::byte > MakeByteSpan(V &&v) noexcept
Definition: span.h:301
User-controlled performance and debug options.
Definition: dbwrapper.h:26
bool force_compact
Compact database on startup.
Definition: dbwrapper.h:28
Application-specific storage settings.
Definition: dbwrapper.h:32
DBOptions options
Passed-through options.
Definition: dbwrapper.h:45
bool obfuscate
If true, store data obfuscated via simple XOR.
Definition: dbwrapper.h:43
bool wipe_data
If true, remove all existing data.
Definition: dbwrapper.h:40
size_t cache_bytes
Configures various leveldb cache settings.
Definition: dbwrapper.h:36
fs::path path
Location in the filesystem where leveldb data will be stored.
Definition: dbwrapper.h:34
bool memory_only
If true, use leveldb's memory environment.
Definition: dbwrapper.h:38