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