LCOV - code coverage report
Current view: top level - src - dbwrapper.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 101 127 79.5 %
Date: 2020-09-26 01:30:44 Functions: 74 92 80.4 %

          Line data    Source code
       1             : // Copyright (c) 2012-2019 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 <serialize.h>
      11             : #include <streams.h>
      12             : #include <util/system.h>
      13             : #include <util/strencodings.h>
      14             : 
      15             : #include <leveldb/db.h>
      16             : #include <leveldb/write_batch.h>
      17             : 
      18             : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
      19             : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
      20             : 
      21           0 : class dbwrapper_error : public std::runtime_error
      22             : {
      23             : public:
      24           0 :     explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
      25             : };
      26             : 
      27             : class CDBWrapper;
      28             : 
      29             : /** These should be considered an implementation detail of the specific database.
      30             :  */
      31             : namespace dbwrapper_private {
      32             : 
      33             : /** Handle database error by throwing dbwrapper_error exception.
      34             :  */
      35             : void HandleError(const leveldb::Status& status);
      36             : 
      37             : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
      38             :  * Database obfuscation should be considered an implementation detail of the
      39             :  * specific database.
      40             :  */
      41             : const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
      42             : 
      43             : };
      44             : 
      45             : /** Batch of changes queued to be written to a CDBWrapper */
      46       17774 : class CDBBatch
      47             : {
      48             :     friend class CDBWrapper;
      49             : 
      50             : private:
      51             :     const CDBWrapper &parent;
      52             :     leveldb::WriteBatch batch;
      53             : 
      54             :     CDataStream ssKey;
      55             :     CDataStream ssValue;
      56             : 
      57             :     size_t size_estimate;
      58             : 
      59             : public:
      60             :     /**
      61             :      * @param[in] _parent   CDBWrapper that this batch is to be submitted to
      62             :      */
      63       17774 :     explicit CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { };
      64             : 
      65           0 :     void Clear()
      66             :     {
      67           0 :         batch.Clear();
      68           0 :         size_estimate = 0;
      69           0 :     }
      70             : 
      71             :     template <typename K, typename V>
      72      194929 :     void Write(const K& key, const V& value)
      73             :     {
      74      194929 :         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
      75      194929 :         ssKey << key;
      76      194929 :         leveldb::Slice slKey(ssKey.data(), ssKey.size());
      77             : 
      78      194929 :         ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
      79      194929 :         ssValue << value;
      80      194929 :         ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
      81      194929 :         leveldb::Slice slValue(ssValue.data(), ssValue.size());
      82             : 
      83      194929 :         batch.Put(slKey, slValue);
      84             :         // LevelDB serializes writes as:
      85             :         // - byte: header
      86             :         // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
      87             :         // - byte[]: key
      88             :         // - varint: value length
      89             :         // - byte[]: value
      90             :         // The formula below assumes the key and value are both less than 16k.
      91      194929 :         size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
      92      194929 :         ssKey.clear();
      93      194929 :         ssValue.clear();
      94      194929 :     }
      95             : 
      96             :     template <typename K>
      97       23487 :     void Erase(const K& key)
      98             :     {
      99       23487 :         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     100       23487 :         ssKey << key;
     101       23487 :         leveldb::Slice slKey(ssKey.data(), ssKey.size());
     102             : 
     103       23487 :         batch.Delete(slKey);
     104             :         // LevelDB serializes erases as:
     105             :         // - byte: header
     106             :         // - varint: key length
     107             :         // - byte[]: key
     108             :         // The formula below assumes the key is less than 16kB.
     109       23487 :         size_estimate += 2 + (slKey.size() > 127) + slKey.size();
     110       23487 :         ssKey.clear();
     111       23487 :     }
     112             : 
     113      232215 :     size_t SizeEstimate() const { return size_estimate; }
     114             : };
     115             : 
     116             : class CDBIterator
     117             : {
     118             : private:
     119             :     const CDBWrapper &parent;
     120             :     leveldb::Iterator *piter;
     121             : 
     122             : public:
     123             : 
     124             :     /**
     125             :      * @param[in] _parent          Parent CDBWrapper instance.
     126             :      * @param[in] _piter           The original leveldb iterator.
     127             :      */
     128        3574 :     CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
     129        3574 :         parent(_parent), piter(_piter) { };
     130             :     ~CDBIterator();
     131             : 
     132             :     bool Valid() const;
     133             : 
     134             :     void SeekToFirst();
     135             : 
     136        1478 :     template<typename K> void Seek(const K& key) {
     137        1478 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     138        1478 :         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     139        1478 :         ssKey << key;
     140        1478 :         leveldb::Slice slKey(ssKey.data(), ssKey.size());
     141        1478 :         piter->Seek(slKey);
     142        1478 :     }
     143             : 
     144             :     void Next();
     145             : 
     146       63383 :     template<typename K> bool GetKey(K& key) {
     147       63383 :         leveldb::Slice slKey = piter->key();
     148             :         try {
     149       63383 :             CDataStream ssKey(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
     150       63383 :             ssKey >> key;
     151       63383 :         } catch (const std::exception&) {
     152             :             return false;
     153         307 :         }
     154       63076 :         return true;
     155       63690 :     }
     156             : 
     157       62886 :     template<typename V> bool GetValue(V& value) {
     158       62886 :         leveldb::Slice slValue = piter->value();
     159             :         try {
     160       62886 :             CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
     161       62886 :             ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
     162       62886 :             ssValue >> value;
     163       62886 :         } catch (const std::exception&) {
     164             :             return false;
     165           0 :         }
     166       62886 :         return true;
     167       62886 :     }
     168             : 
     169           0 :     unsigned int GetValueSize() {
     170           0 :         return piter->value().size();
     171             :     }
     172             : 
     173             : };
     174             : 
     175             : class CDBWrapper
     176             : {
     177             :     friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
     178             : private:
     179             :     //! custom environment this database is using (may be nullptr in case of default environment)
     180             :     leveldb::Env* penv;
     181             : 
     182             :     //! database options used
     183             :     leveldb::Options options;
     184             : 
     185             :     //! options used when reading from the database
     186             :     leveldb::ReadOptions readoptions;
     187             : 
     188             :     //! options used when iterating over values of the database
     189             :     leveldb::ReadOptions iteroptions;
     190             : 
     191             :     //! options used when writing to the database
     192             :     leveldb::WriteOptions writeoptions;
     193             : 
     194             :     //! options used when sync writing to the database
     195             :     leveldb::WriteOptions syncoptions;
     196             : 
     197             :     //! the database itself
     198             :     leveldb::DB* pdb;
     199             : 
     200             :     //! the name of this database
     201             :     std::string m_name;
     202             : 
     203             :     //! a key used for optional XOR-obfuscation of the database
     204             :     std::vector<unsigned char> obfuscate_key;
     205             : 
     206             :     //! the key under which the obfuscation key is stored
     207             :     static const std::string OBFUSCATE_KEY_KEY;
     208             : 
     209             :     //! the length of the obfuscate key in number of bytes
     210             :     static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
     211             : 
     212             :     std::vector<unsigned char> CreateObfuscateKey() const;
     213             : 
     214             : public:
     215             :     /**
     216             :      * @param[in] path        Location in the filesystem where leveldb data will be stored.
     217             :      * @param[in] nCacheSize  Configures various leveldb cache settings.
     218             :      * @param[in] fMemory     If true, use leveldb's memory environment.
     219             :      * @param[in] fWipe       If true, remove all existing data.
     220             :      * @param[in] obfuscate   If true, store data obfuscated via simple XOR. If false, XOR
     221             :      *                        with a zero'd byte array.
     222             :      */
     223             :     CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
     224             :     ~CDBWrapper();
     225             : 
     226             :     CDBWrapper(const CDBWrapper&) = delete;
     227             :     CDBWrapper& operator=(const CDBWrapper&) = delete;
     228             : 
     229             :     template <typename K, typename V>
     230     5806686 :     bool Read(const K& key, V& value) const
     231             :     {
     232     5806686 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     233     5806686 :         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     234     5806686 :         ssKey << key;
     235     5806686 :         leveldb::Slice slKey(ssKey.data(), ssKey.size());
     236             : 
     237     5806686 :         std::string strValue;
     238     5806686 :         leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
     239     5806686 :         if (!status.ok()) {
     240     5712359 :             if (status.IsNotFound())
     241     5712359 :                 return false;
     242           0 :             LogPrintf("LevelDB read failure: %s\n", status.ToString());
     243           0 :             dbwrapper_private::HandleError(status);
     244             :         }
     245             :         try {
     246       94327 :             CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
     247       94327 :             ssValue.Xor(obfuscate_key);
     248       94327 :             ssValue >> value;
     249       94327 :         } catch (const std::exception&) {
     250             :             return false;
     251           0 :         }
     252       94327 :         return true;
     253     5806686 :     }
     254             : 
     255             :     template <typename K, typename V>
     256        5021 :     bool Write(const K& key, const V& value, bool fSync = false)
     257             :     {
     258        5021 :         CDBBatch batch(*this);
     259        5021 :         batch.Write(key, value);
     260        5021 :         return WriteBatch(batch, fSync);
     261        5021 :     }
     262             : 
     263             :     template <typename K>
     264         495 :     bool Exists(const K& key) const
     265             :     {
     266         495 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     267         495 :         ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     268         495 :         ssKey << key;
     269         495 :         leveldb::Slice slKey(ssKey.data(), ssKey.size());
     270             : 
     271         495 :         std::string strValue;
     272         495 :         leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
     273         495 :         if (!status.ok()) {
     274         495 :             if (status.IsNotFound())
     275         495 :                 return false;
     276           0 :             LogPrintf("LevelDB read failure: %s\n", status.ToString());
     277           0 :             dbwrapper_private::HandleError(status);
     278             :         }
     279           0 :         return true;
     280         495 :     }
     281             : 
     282             :     template <typename K>
     283           8 :     bool Erase(const K& key, bool fSync = false)
     284             :     {
     285           8 :         CDBBatch batch(*this);
     286           8 :         batch.Erase(key);
     287           8 :         return WriteBatch(batch, fSync);
     288           8 :     }
     289             : 
     290             :     bool WriteBatch(CDBBatch& batch, bool fSync = false);
     291             : 
     292             :     // Get an estimate of LevelDB memory usage (in bytes).
     293             :     size_t DynamicMemoryUsage() const;
     294             : 
     295        1787 :     CDBIterator *NewIterator()
     296             :     {
     297        1787 :         return new CDBIterator(*this, pdb->NewIterator(iteroptions));
     298           0 :     }
     299             : 
     300             :     /**
     301             :      * Return true if the database managed by this class contains no entries.
     302             :      */
     303             :     bool IsEmpty();
     304             : 
     305             :     template<typename K>
     306           7 :     size_t EstimateSize(const K& key_begin, const K& key_end) const
     307             :     {
     308           7 :         CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
     309           7 :         ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     310           7 :         ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     311           7 :         ssKey1 << key_begin;
     312           7 :         ssKey2 << key_end;
     313           7 :         leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
     314           7 :         leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
     315           7 :         uint64_t size = 0;
     316           7 :         leveldb::Range range(slKey1, slKey2);
     317           7 :         pdb->GetApproximateSizes(&range, 1, &size);
     318           7 :         return size;
     319           7 :     }
     320             : 
     321             :     /**
     322             :      * Compact a certain range of keys in the database.
     323             :      */
     324             :     template<typename K>
     325           0 :     void CompactRange(const K& key_begin, const K& key_end) const
     326             :     {
     327           0 :         CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
     328           0 :         ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     329           0 :         ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
     330           0 :         ssKey1 << key_begin;
     331           0 :         ssKey2 << key_end;
     332           0 :         leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
     333           0 :         leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
     334           0 :         pdb->CompactRange(&slKey1, &slKey2);
     335           0 :     }
     336             : 
     337             : };
     338             : 
     339             : #endif // BITCOIN_DBWRAPPER_H

Generated by: LCOV version 1.15