dbwrapper.h raw

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