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