Bitcoin ABC  0.29.2
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 <fs.h>
10 #include <serialize.h>
11 #include <span.h>
12 #include <streams.h>
13 #include <util/strencodings.h>
14 #include <util/system.h>
15 
16 #include <leveldb/db.h>
17 #include <leveldb/write_batch.h>
18 
19 #include <optional>
20 
21 static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
22 static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
23 
24 class dbwrapper_error : public std::runtime_error {
25 public:
26  explicit dbwrapper_error(const std::string &msg)
27  : std::runtime_error(msg) {}
28 };
29 
30 class CDBWrapper;
31 
32 namespace dbwrapper {
33 using leveldb::DestroyDB;
34 }
38 namespace dbwrapper_private {
39 
43 void HandleError(const leveldb::Status &status);
44 
50 const std::vector<uint8_t> &GetObfuscateKey(const CDBWrapper &w);
51 }; // namespace dbwrapper_private
52 
54 class CDBBatch {
55  friend class CDBWrapper;
56 
57 private:
59  leveldb::WriteBatch batch;
60 
63 
64  size_t size_estimate;
65 
66 public:
70  explicit CDBBatch(const CDBWrapper &_parent)
71  : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION),
73 
74  void Clear() {
75  batch.Clear();
76  size_estimate = 0;
77  }
78 
79  template <typename K, typename V> void Write(const K &key, const V &value) {
81  ssKey << key;
82  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
83 
85  ssValue << value;
87  leveldb::Slice slValue((const char *)ssValue.data(), ssValue.size());
88 
89  batch.Put(slKey, slValue);
90  // LevelDB serializes writes as:
91  // - byte: header
92  // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
93  // - byte[]: key
94  // - varint: value length
95  // - byte[]: value
96  // The formula below assumes the key and value are both less than 16k.
97  size_estimate += 3 + (slKey.size() > 127) + slKey.size() +
98  (slValue.size() > 127) + slValue.size();
99  ssKey.clear();
100  ssValue.clear();
101  }
102 
103  template <typename K> void Erase(const K &key) {
105  ssKey << key;
106  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
107 
108  batch.Delete(slKey);
109  // LevelDB serializes erases as:
110  // - byte: header
111  // - varint: key length
112  // - byte[]: key
113  // The formula below assumes the key is less than 16kB.
114  size_estimate += 2 + (slKey.size() > 127) + slKey.size();
115  ssKey.clear();
116  }
117 
118  size_t SizeEstimate() const { return size_estimate; }
119 };
120 
121 class CDBIterator {
122 private:
124  leveldb::Iterator *piter;
125 
126 public:
131  CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter)
132  : parent(_parent), piter(_piter){};
133  ~CDBIterator();
134 
135  bool Valid() const;
136 
137  void SeekToFirst();
138 
139  template <typename K> void Seek(const K &key) {
142  ssKey << key;
143  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
144  piter->Seek(slKey);
145  }
146 
147  void Next();
148 
149  template <typename K> bool GetKey(K &key) {
150  leveldb::Slice slKey = piter->key();
151  try {
153  ssKey >> key;
154  } catch (const std::exception &) {
155  return false;
156  }
157  return true;
158  }
159 
160  template <typename V> bool GetValue(V &value) {
161  leveldb::Slice slValue = piter->value();
162  try {
163  CDataStream ssValue{MakeByteSpan(slValue), SER_DISK,
166  ssValue >> value;
167  } catch (const std::exception &) {
168  return false;
169  }
170  return true;
171  }
172 
173  unsigned int GetValueSize() { return piter->value().size(); }
174 };
175 
176 class CDBWrapper {
177  friend const std::vector<uint8_t> &
179 
180 private:
183  leveldb::Env *penv;
184 
186  leveldb::Options options;
187 
189  leveldb::ReadOptions readoptions;
190 
192  leveldb::ReadOptions iteroptions;
193 
195  leveldb::WriteOptions writeoptions;
196 
198  leveldb::WriteOptions syncoptions;
199 
201  leveldb::DB *pdb;
202 
204  std::string m_name;
205 
207  std::vector<uint8_t> obfuscate_key;
208 
210  static const std::string OBFUSCATE_KEY_KEY;
211 
213  static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
214 
215  std::vector<uint8_t> CreateObfuscateKey() const;
216 
219 
222 
223 public:
234  CDBWrapper(const fs::path &path, size_t nCacheSize, bool fMemory = false,
235  bool fWipe = false, bool obfuscate = false);
236  ~CDBWrapper();
237 
238  CDBWrapper(const CDBWrapper &) = delete;
239  CDBWrapper &operator=(const CDBWrapper &) = delete;
240 
241  template <typename K, typename V> bool Read(const K &key, V &value) const {
244  ssKey << key;
245  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
246 
247  std::string strValue;
248  leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
249  if (!status.ok()) {
250  if (status.IsNotFound()) return false;
251  LogPrintf("LevelDB read failure: %s\n", status.ToString());
253  }
254  try {
255  CDataStream ssValue{MakeByteSpan(strValue), SER_DISK,
257  ssValue.Xor(obfuscate_key);
258  ssValue >> value;
259  } catch (const std::exception &) {
260  return false;
261  }
262  return true;
263  }
264 
265  template <typename K, typename V>
266  bool Write(const K &key, const V &value, bool fSync = false) {
267  CDBBatch batch(*this);
268  batch.Write(key, value);
269  return WriteBatch(batch, fSync);
270  }
271 
273  std::optional<fs::path> StoragePath() {
274  if (m_is_memory) {
275  return {};
276  }
277  return m_path;
278  }
279 
280  template <typename K> bool Exists(const K &key) const {
283  ssKey << key;
284  leveldb::Slice slKey((const char *)ssKey.data(), ssKey.size());
285 
286  std::string strValue;
287  leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
288  if (!status.ok()) {
289  if (status.IsNotFound()) return false;
290  LogPrintf("LevelDB read failure: %s\n", status.ToString());
292  }
293  return true;
294  }
295 
296  template <typename K> bool Erase(const K &key, bool fSync = false) {
297  CDBBatch batch(*this);
298  batch.Erase(key);
299  return WriteBatch(batch, fSync);
300  }
301 
302  bool WriteBatch(CDBBatch &batch, bool fSync = false);
303 
304  // Get an estimate of LevelDB memory usage (in bytes).
305  size_t DynamicMemoryUsage() const;
306 
308  return new CDBIterator(*this, pdb->NewIterator(iteroptions));
309  }
310 
314  bool IsEmpty();
315 
316  template <typename K>
317  size_t EstimateSize(const K &key_begin, const K &key_end) const {
319  ssKey2(SER_DISK, CLIENT_VERSION);
320  ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
322  ssKey1 << key_begin;
323  ssKey2 << key_end;
324  leveldb::Slice slKey1((const char *)ssKey1.data(), ssKey1.size());
325  leveldb::Slice slKey2((const char *)ssKey2.data(), ssKey2.size());
326  uint64_t size = 0;
327  leveldb::Range range(slKey1, slKey2);
328  pdb->GetApproximateSizes(&range, 1, &size);
329  return size;
330  }
331 
335  template <typename K>
336  void CompactRange(const K &key_begin, const K &key_end) const {
338  ssKey2(SER_DISK, CLIENT_VERSION);
339  ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
341  ssKey1 << key_begin;
342  ssKey2 << key_end;
343  leveldb::Slice slKey1((const char *)ssKey1.data(), ssKey1.size());
344  leveldb::Slice slKey2((const char *)ssKey2.data(), ssKey2.size());
345  pdb->CompactRange(&slKey1, &slKey2);
346  }
347 };
348 
349 #endif // BITCOIN_DBWRAPPER_H
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:54
void Erase(const K &key)
Definition: dbwrapper.h:103
size_t SizeEstimate() const
Definition: dbwrapper.h:118
CDataStream ssKey
Definition: dbwrapper.h:61
CDataStream ssValue
Definition: dbwrapper.h:62
size_t size_estimate
Definition: dbwrapper.h:64
void Write(const K &key, const V &value)
Definition: dbwrapper.h:79
void Clear()
Definition: dbwrapper.h:74
CDBBatch(const CDBWrapper &_parent)
Definition: dbwrapper.h:70
leveldb::WriteBatch batch
Definition: dbwrapper.h:59
const CDBWrapper & parent
Definition: dbwrapper.h:58
bool GetValue(V &value)
Definition: dbwrapper.h:160
unsigned int GetValueSize()
Definition: dbwrapper.h:173
bool GetKey(K &key)
Definition: dbwrapper.h:149
leveldb::Iterator * piter
Definition: dbwrapper.h:124
void Seek(const K &key)
Definition: dbwrapper.h:139
const CDBWrapper & parent
Definition: dbwrapper.h:123
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:131
CDBWrapper(const CDBWrapper &)=delete
CDBIterator * NewIterator()
Definition: dbwrapper.h:307
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:183
bool WriteBatch(CDBBatch &batch, bool fSync=false)
Definition: dbwrapper.cpp:195
bool Read(const K &key, V &value) const
Definition: dbwrapper.h:241
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:204
bool Erase(const K &key, bool fSync=false)
Definition: dbwrapper.h:296
bool Write(const K &key, const V &value, bool fSync=false)
Definition: dbwrapper.h:266
bool Exists(const K &key) const
Definition: dbwrapper.h:280
std::vector< uint8_t > obfuscate_key
a key used for optional XOR-obfuscation of the database
Definition: dbwrapper.h:207
CDBWrapper & operator=(const CDBWrapper &)=delete
std::optional< fs::path > StoragePath()
Definition: dbwrapper.h:273
leveldb::Options options
database options used
Definition: dbwrapper.h:186
static const unsigned int OBFUSCATE_KEY_NUM_BYTES
the length of the obfuscate key in number of bytes
Definition: dbwrapper.h:213
static const std::string OBFUSCATE_KEY_KEY
the key under which the obfuscation key is stored
Definition: dbwrapper.h:210
leveldb::WriteOptions writeoptions
options used when writing to the database
Definition: dbwrapper.h:195
const fs::path m_path
path to filesystem storage
Definition: dbwrapper.h:218
leveldb::WriteOptions syncoptions
options used when sync writing to the database
Definition: dbwrapper.h:198
CDBWrapper(const fs::path &path, size_t nCacheSize, bool fMemory=false, bool fWipe=false, bool obfuscate=false)
Definition: dbwrapper.cpp:119
bool m_is_memory
whether or not the database resides in memory
Definition: dbwrapper.h:221
leveldb::DB * pdb
the database itself
Definition: dbwrapper.h:201
leveldb::ReadOptions iteroptions
options used when iterating over values of the database
Definition: dbwrapper.h:192
void CompactRange(const K &key_begin, const K &key_end) const
Compact a certain range of keys in the database.
Definition: dbwrapper.h:336
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:189
size_t EstimateSize(const K &key_begin, const K &key_end) const
Definition: dbwrapper.h:317
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:26
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:21
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE
Definition: dbwrapper.h:22
#define LogPrintf(...)
Definition: logging.h:206
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