/ src / dbwrapper.h
dbwrapper.h
  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 <attributes.h>
  9  #include <serialize.h>
 10  #include <span.h>
 11  #include <streams.h>
 12  #include <util/check.h>
 13  #include <util/fs.h>
 14  
 15  #include <cstddef>
 16  #include <exception>
 17  #include <memory>
 18  #include <optional>
 19  #include <stdexcept>
 20  #include <string>
 21  #include <vector>
 22  
 23  static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
 24  static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
 25  
 26  //! User-controlled performance and debug options.
 27  struct DBOptions {
 28      //! Compact database on startup.
 29      bool force_compact = false;
 30  };
 31  
 32  //! Application-specific storage settings.
 33  struct DBParams {
 34      //! Location in the filesystem where leveldb data will be stored.
 35      fs::path path;
 36      //! Configures various leveldb cache settings.
 37      size_t cache_bytes;
 38      //! If true, use leveldb's memory environment.
 39      bool memory_only = false;
 40      //! If true, remove all existing data.
 41      bool wipe_data = false;
 42      //! If true, store data obfuscated via simple XOR. If false, XOR with a
 43      //! zero'd byte array.
 44      bool obfuscate = false;
 45      //! Passed-through options.
 46      DBOptions options{};
 47  };
 48  
 49  class dbwrapper_error : public std::runtime_error
 50  {
 51  public:
 52      explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
 53  };
 54  
 55  class CDBWrapper;
 56  
 57  /** These should be considered an implementation detail of the specific database.
 58   */
 59  namespace dbwrapper_private {
 60  
 61  /** Work around circular dependency, as well as for testing in dbwrapper_tests.
 62   * Database obfuscation should be considered an implementation detail of the
 63   * specific database.
 64   */
 65  const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
 66  
 67  }; // namespace dbwrapper_private
 68  
 69  bool DestroyDB(const std::string& path_str);
 70  
 71  /** Batch of changes queued to be written to a CDBWrapper */
 72  class CDBBatch
 73  {
 74      friend class CDBWrapper;
 75  
 76  private:
 77      const CDBWrapper &parent;
 78  
 79      struct WriteBatchImpl;
 80      const std::unique_ptr<WriteBatchImpl> m_impl_batch;
 81  
 82      DataStream ssKey{};
 83      DataStream ssValue{};
 84  
 85      size_t size_estimate{0};
 86  
 87      void WriteImpl(Span<const std::byte> key, DataStream& ssValue);
 88      void EraseImpl(Span<const std::byte> key);
 89  
 90  public:
 91      /**
 92       * @param[in] _parent   CDBWrapper that this batch is to be submitted to
 93       */
 94      explicit CDBBatch(const CDBWrapper& _parent);
 95      ~CDBBatch();
 96      void Clear();
 97  
 98      template <typename K, typename V>
 99      void Write(const K& key, const V& value)
100      {
101          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
102          ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
103          ssKey << key;
104          ssValue << value;
105          WriteImpl(ssKey, ssValue);
106          ssKey.clear();
107          ssValue.clear();
108      }
109  
110      template <typename K>
111      void Erase(const K& key)
112      {
113          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
114          ssKey << key;
115          EraseImpl(ssKey);
116          ssKey.clear();
117      }
118  
119      size_t SizeEstimate() const { return size_estimate; }
120  };
121  
122  class CDBIterator
123  {
124  public:
125      struct IteratorImpl;
126  
127  private:
128      const CDBWrapper &parent;
129      const std::unique_ptr<IteratorImpl> m_impl_iter;
130  
131      void SeekImpl(Span<const std::byte> key);
132      Span<const std::byte> GetKeyImpl() const;
133      Span<const std::byte> GetValueImpl() const;
134  
135  public:
136  
137      /**
138       * @param[in] _parent          Parent CDBWrapper instance.
139       * @param[in] _piter           The original leveldb iterator.
140       */
141      CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
142      ~CDBIterator();
143  
144      bool Valid() const;
145  
146      void SeekToFirst();
147  
148      template<typename K> void Seek(const K& key) {
149          DataStream ssKey{};
150          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
151          ssKey << key;
152          SeekImpl(ssKey);
153      }
154  
155      void Next();
156  
157      template<typename K> bool GetKey(K& key) {
158          try {
159              DataStream ssKey{GetKeyImpl()};
160              ssKey >> key;
161          } catch (const std::exception&) {
162              return false;
163          }
164          return true;
165      }
166  
167      template<typename V> bool GetValue(V& value) {
168          try {
169              DataStream ssValue{GetValueImpl()};
170              ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
171              ssValue >> value;
172          } catch (const std::exception&) {
173              return false;
174          }
175          return true;
176      }
177  };
178  
179  struct LevelDBContext;
180  
181  class CDBWrapper
182  {
183      friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
184  private:
185      //! holds all leveldb-specific fields of this class
186      std::unique_ptr<LevelDBContext> m_db_context;
187  
188      //! the name of this database
189      std::string m_name;
190  
191      //! a key used for optional XOR-obfuscation of the database
192      std::vector<unsigned char> obfuscate_key;
193  
194      //! the key under which the obfuscation key is stored
195      static const std::string OBFUSCATE_KEY_KEY;
196  
197      //! the length of the obfuscate key in number of bytes
198      static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
199  
200      std::vector<unsigned char> CreateObfuscateKey() const;
201  
202      //! path to filesystem storage
203      const fs::path m_path;
204  
205      //! whether or not the database resides in memory
206      bool m_is_memory;
207  
208      std::optional<std::string> ReadImpl(Span<const std::byte> key) const;
209      bool ExistsImpl(Span<const std::byte> key) const;
210      size_t EstimateSizeImpl(Span<const std::byte> key1, Span<const std::byte> key2) const;
211      auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
212  
213  public:
214      CDBWrapper(const DBParams& params);
215      ~CDBWrapper();
216  
217      CDBWrapper(const CDBWrapper&) = delete;
218      CDBWrapper& operator=(const CDBWrapper&) = delete;
219  
220      template <typename K, typename V>
221      bool Read(const K& key, V& value) const
222      {
223          DataStream ssKey{};
224          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
225          ssKey << key;
226          std::optional<std::string> strValue{ReadImpl(ssKey)};
227          if (!strValue) {
228              return false;
229          }
230          try {
231              DataStream ssValue{MakeByteSpan(*strValue)};
232              ssValue.Xor(obfuscate_key);
233              ssValue >> value;
234          } catch (const std::exception&) {
235              return false;
236          }
237          return true;
238      }
239  
240      template <typename K, typename V>
241      bool Write(const K& key, const V& value, bool fSync = false)
242      {
243          CDBBatch batch(*this);
244          batch.Write(key, value);
245          return WriteBatch(batch, fSync);
246      }
247  
248      //! @returns filesystem path to the on-disk data.
249      std::optional<fs::path> StoragePath() {
250          if (m_is_memory) {
251              return {};
252          }
253          return m_path;
254      }
255  
256      template <typename K>
257      bool Exists(const K& key) const
258      {
259          DataStream ssKey{};
260          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
261          ssKey << key;
262          return ExistsImpl(ssKey);
263      }
264  
265      template <typename K>
266      bool Erase(const K& key, bool fSync = false)
267      {
268          CDBBatch batch(*this);
269          batch.Erase(key);
270          return WriteBatch(batch, fSync);
271      }
272  
273      bool WriteBatch(CDBBatch& batch, bool fSync = false);
274  
275      // Get an estimate of LevelDB memory usage (in bytes).
276      size_t DynamicMemoryUsage() const;
277  
278      CDBIterator* NewIterator();
279  
280      /**
281       * Return true if the database managed by this class contains no entries.
282       */
283      bool IsEmpty();
284  
285      template<typename K>
286      size_t EstimateSize(const K& key_begin, const K& key_end) const
287      {
288          DataStream ssKey1{}, ssKey2{};
289          ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
290          ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
291          ssKey1 << key_begin;
292          ssKey2 << key_end;
293          return EstimateSizeImpl(ssKey1, ssKey2);
294      }
295  };
296  
297  #endif // BITCOIN_DBWRAPPER_H