/ src / dbwrapper.h
dbwrapper.h
  1  // Copyright (c) 2012-present 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  
 22  static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
 23  static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
 24  static const size_t DBWRAPPER_MAX_FILE_SIZE = 32 << 20; // 32 MiB
 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 Obfuscation& GetObfuscation(const CDBWrapper&);
 66  }; // namespace dbwrapper_private
 67  
 68  bool DestroyDB(const std::string& path_str);
 69  
 70  /** Batch of changes queued to be written to a CDBWrapper */
 71  class CDBBatch
 72  {
 73      friend class CDBWrapper;
 74  
 75  private:
 76      const CDBWrapper &parent;
 77  
 78      struct WriteBatchImpl;
 79      const std::unique_ptr<WriteBatchImpl> m_impl_batch;
 80  
 81      DataStream ssKey{};
 82      DataStream ssValue{};
 83  
 84      void WriteImpl(std::span<const std::byte> key, DataStream& ssValue);
 85      void EraseImpl(std::span<const std::byte> key);
 86  
 87  public:
 88      /**
 89       * @param[in] _parent   CDBWrapper that this batch is to be submitted to
 90       */
 91      explicit CDBBatch(const CDBWrapper& _parent);
 92      ~CDBBatch();
 93      void Clear();
 94  
 95      template <typename K, typename V>
 96      void Write(const K& key, const V& value)
 97      {
 98          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
 99          ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
100          ssKey << key;
101          ssValue << value;
102          WriteImpl(ssKey, ssValue);
103          ssKey.clear();
104          ssValue.clear();
105      }
106  
107      template <typename K>
108      void Erase(const K& key)
109      {
110          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
111          ssKey << key;
112          EraseImpl(ssKey);
113          ssKey.clear();
114      }
115  
116      size_t ApproximateSize() const;
117  };
118  
119  class CDBIterator
120  {
121  public:
122      struct IteratorImpl;
123  
124  private:
125      const CDBWrapper &parent;
126      const std::unique_ptr<IteratorImpl> m_impl_iter;
127  
128      void SeekImpl(std::span<const std::byte> key);
129      std::span<const std::byte> GetKeyImpl() const;
130      std::span<const std::byte> GetValueImpl() const;
131  
132  public:
133  
134      /**
135       * @param[in] _parent          Parent CDBWrapper instance.
136       * @param[in] _piter           The original leveldb iterator.
137       */
138      CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
139      ~CDBIterator();
140  
141      bool Valid() const;
142  
143      void SeekToFirst();
144  
145      template<typename K> void Seek(const K& key) {
146          DataStream ssKey{};
147          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
148          ssKey << key;
149          SeekImpl(ssKey);
150      }
151  
152      void Next();
153  
154      template<typename K> bool GetKey(K& key) {
155          try {
156              DataStream ssKey{GetKeyImpl()};
157              ssKey >> key;
158          } catch (const std::exception&) {
159              return false;
160          }
161          return true;
162      }
163  
164      template<typename V> bool GetValue(V& value) {
165          try {
166              DataStream ssValue{GetValueImpl()};
167              dbwrapper_private::GetObfuscation(parent)(ssValue);
168              ssValue >> value;
169          } catch (const std::exception&) {
170              return false;
171          }
172          return true;
173      }
174  };
175  
176  struct LevelDBContext;
177  
178  class CDBWrapper
179  {
180      friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
181  private:
182      //! holds all leveldb-specific fields of this class
183      std::unique_ptr<LevelDBContext> m_db_context;
184  
185      //! the name of this database
186      std::string m_name;
187  
188      //! optional XOR-obfuscation of the database
189      Obfuscation m_obfuscation;
190  
191      //! obfuscation key storage key, null-prefixed to avoid collisions
192      inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
193  
194      std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
195      bool ExistsImpl(std::span<const std::byte> key) const;
196      size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
197      auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
198  
199  public:
200      CDBWrapper(const DBParams& params);
201      ~CDBWrapper();
202  
203      CDBWrapper(const CDBWrapper&) = delete;
204      CDBWrapper& operator=(const CDBWrapper&) = delete;
205  
206      template <typename K, typename V>
207      bool Read(const K& key, V& value) const
208      {
209          DataStream ssKey{};
210          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
211          ssKey << key;
212          std::optional<std::string> strValue{ReadImpl(ssKey)};
213          if (!strValue) {
214              return false;
215          }
216          try {
217              std::span ssValue{MakeWritableByteSpan(*strValue)};
218              m_obfuscation(ssValue);
219              SpanReader{ssValue} >> value;
220          } catch (const std::exception&) {
221              return false;
222          }
223          return true;
224      }
225  
226      template <typename K, typename V>
227      void Write(const K& key, const V& value, bool fSync = false)
228      {
229          CDBBatch batch(*this);
230          batch.Write(key, value);
231          WriteBatch(batch, fSync);
232      }
233  
234      template <typename K>
235      bool Exists(const K& key) const
236      {
237          DataStream ssKey{};
238          ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
239          ssKey << key;
240          return ExistsImpl(ssKey);
241      }
242  
243      template <typename K>
244      void Erase(const K& key, bool fSync = false)
245      {
246          CDBBatch batch(*this);
247          batch.Erase(key);
248          WriteBatch(batch, fSync);
249      }
250  
251      void WriteBatch(CDBBatch& batch, bool fSync = false);
252  
253      // Get an estimate of LevelDB memory usage (in bytes).
254      size_t DynamicMemoryUsage() const;
255  
256      CDBIterator* NewIterator();
257  
258      /**
259       * Return true if the database managed by this class contains no entries.
260       */
261      bool IsEmpty();
262  
263      template<typename K>
264      size_t EstimateSize(const K& key_begin, const K& key_end) const
265      {
266          DataStream ssKey1{}, ssKey2{};
267          ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
268          ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
269          ssKey1 << key_begin;
270          ssKey2 << key_end;
271          return EstimateSizeImpl(ssKey1, ssKey2);
272      }
273  };
274  
275  #endif // BITCOIN_DBWRAPPER_H