LCOV - code coverage report
Current view: top level - src/wallet - db.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 116 132 87.9 %
Date: 2025-02-23 09:33:43 Functions: 35 41 85.4 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2021 The Bitcoin developers
       3             : // Copyright (c) 2019-2021 The PIVX Core developers
       4             : // Distributed under the MIT software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #ifndef PIVX_WALLET_DB_H
       8             : #define PIVX_WALLET_DB_H
       9             : 
      10             : #include "clientversion.h"
      11             : #include "fs.h"
      12             : #include "serialize.h"
      13             : #include "streams.h"
      14             : #include "sync.h"
      15             : #include "version.h"
      16             : 
      17             : #include <atomic>
      18             : #include <map>
      19             : #include <string>
      20             : #include <unordered_map>
      21             : #include <vector>
      22             : 
      23             : #include <db_cxx.h>
      24             : 
      25             : static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100;
      26             : static const bool DEFAULT_WALLET_PRIVDB = true;
      27             : 
      28             : struct WalletDatabaseFileId {
      29             :     u_int8_t value[DB_FILE_ID_LEN];
      30             :     bool operator==(const WalletDatabaseFileId& rhs) const;
      31             : };
      32             : 
      33             : class BerkeleyEnvironment
      34             : {
      35             : private:
      36             :     bool fDbEnvInit;
      37             :     bool fMockDb;
      38             :     // Don't change into fs::path, as that can result in
      39             :     // shutdown problems/crashes caused by a static initialized internal pointer.
      40             :     std::string strPath;
      41             : 
      42             : public:
      43             :     std::unique_ptr<DbEnv> dbenv;
      44             :     std::map<std::string, int> mapFileUseCount;
      45             :     std::map<std::string, Db*> mapDb;
      46             :     std::unordered_map<std::string, WalletDatabaseFileId> m_fileids;
      47             :     std::condition_variable_any m_db_in_use;
      48             : 
      49             :     explicit BerkeleyEnvironment(const fs::path& env_directory);
      50             :     ~BerkeleyEnvironment();
      51             :     void Reset();
      52             : 
      53             :     void MakeMock();
      54        2749 :     bool IsMock() const { return fMockDb; }
      55             :     bool IsInitialized() const { return fDbEnvInit; }
      56        2822 :     fs::path Directory() const { return strPath; }
      57             : 
      58             :     /**
      59             :      * Verify that database file strFile is OK. If it is not,
      60             :      * call the callback to try to recover.
      61             :      * This must be called BEFORE strFile is opened.
      62             :      * Returns true if strFile is OK.
      63             :      */
      64             :     enum VerifyResult { VERIFY_OK,
      65             :                         RECOVER_OK,
      66             :                         RECOVER_FAIL };
      67             :     typedef bool (*recoverFunc_type)(const fs::path& file_path, std::string& out_backup_filename);
      68             :     VerifyResult Verify(const std::string& strFile, recoverFunc_type recoverFunc, std::string& out_backup_filename);
      69             : 
      70             :     /**
      71             :      * Salvage data from a file that Verify says is bad.
      72             :      * fAggressive sets the DB_AGGRESSIVE flag (see berkeley DB->verify() method documentation).
      73             :      * Appends binary key/value pairs to vResult, returns true if successful.
      74             :      * NOTE: reads the entire database into memory, so cannot be used
      75             :      * for huge databases.
      76             :      */
      77             :     typedef std::pair<std::vector<unsigned char>, std::vector<unsigned char> > KeyValPair;
      78             :     bool Salvage(const std::string& strFile, bool fAggressive, std::vector<KeyValPair>& vResult);
      79             : 
      80             :     bool Open(bool retry);
      81             :     void Close();
      82             :     void Flush(bool fShutdown);
      83             :     void CheckpointLSN(const std::string& strFile);
      84             : 
      85             :     void CloseDb(const std::string& strFile);
      86             :     void ReloadDbEnv();
      87             : 
      88         334 :     DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC)
      89             :     {
      90         334 :         DbTxn* ptxn = nullptr;
      91         668 :         int ret = dbenv->txn_begin(nullptr, &ptxn, flags);
      92         334 :         if (!ptxn || ret != 0)
      93           0 :             return nullptr;
      94             :         return ptxn;
      95             :     }
      96             : };
      97             : 
      98             : /** Get BerkeleyEnvironment and database filename given a wallet path. */
      99             : BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename);
     100             : 
     101             : /** An instance of this class represents one database.
     102             :  * For BerkeleyDB this is just a (env, strFile) tuple.
     103             :  **/
     104         504 : class BerkeleyDatabase
     105             : {
     106             :     friend class BerkeleyBatch;
     107             : public:
     108             :     /** Create dummy DB handle */
     109          96 :     BerkeleyDatabase() : nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0), env(nullptr)
     110             :     {
     111             :     }
     112             : 
     113             :     /** Create DB handle to real database */
     114         409 :     explicit BerkeleyDatabase(const fs::path& wallet_path, bool mock = false) :
     115         409 :         nUpdateCounter(0), nLastSeen(0), nLastFlushed(0), nLastWalletUpdate(0)
     116             :     {
     117         409 :         env = GetWalletEnv(wallet_path, strFile);
     118         409 :         if (mock) {
     119          39 :             env->Close();
     120          39 :             env->Reset();
     121          39 :             env->MakeMock();
     122             :         }
     123         409 :     }
     124             : 
     125             :     /** Return object for accessing database at specified path. */
     126         370 :     static std::unique_ptr<BerkeleyDatabase> Create(const fs::path& path)
     127             :     {
     128         370 :         return std::make_unique<BerkeleyDatabase>(path);
     129             :     }
     130             : 
     131             :     /** Return object for accessing dummy database with no read/write capabilities. */
     132           8 :     static std::unique_ptr<BerkeleyDatabase> CreateDummy()
     133             :     {
     134           8 :         return std::make_unique<BerkeleyDatabase>();
     135             :     }
     136             : 
     137             :     /** Return object for accessing temporary in-memory database. */
     138          39 :     static std::unique_ptr<BerkeleyDatabase> CreateMock()
     139             :     {
     140          39 :         return std::make_unique<BerkeleyDatabase>("", true /* mock */);
     141             :     }
     142             : 
     143             :     /** Rewrite the entire database on disk, with the exception of key pszSkip if non-zero
     144             :      */
     145             :     bool Rewrite(const char* pszSkip=nullptr);
     146             : 
     147             :     /** Back up the entire database to a file.
     148             :      */
     149             :     bool Backup(const std::string& strDest);
     150             : 
     151             :     /** Make sure all changes are flushed to disk.
     152             :      */
     153             :     void Flush(bool shutdown);
     154             : 
     155             :     void IncrementUpdateCounter();
     156             : 
     157             :     void ReloadDbEnv();
     158             : 
     159             :     std::atomic<unsigned int> nUpdateCounter;
     160             :     unsigned int nLastSeen;
     161             :     unsigned int nLastFlushed;
     162             :     int64_t nLastWalletUpdate;
     163             :     fs::path GetPathToFile() { return env->Directory() / strFile; }
     164             : 
     165             : private:
     166             :     /** BerkeleyDB specific */
     167             :     BerkeleyEnvironment *env;
     168             :     std::string strFile;
     169             : 
     170             :     /** Return whether this database handle is a dummy for testing.
     171             :      * Only to be used at a low level, application should ideally not care
     172             :      * about this.
     173             :      */
     174      464014 :     bool IsDummy() { return env == nullptr; }
     175             : };
     176             : 
     177             : 
     178             : /** RAII class that provides access to a Berkeley database */
     179             : class BerkeleyBatch
     180             : {
     181             : protected:
     182             :     Db* pdb;
     183             :     std::string strFile;
     184             :     DbTxn* activeTxn;
     185             :     bool fReadOnly;
     186             :     bool fFlushOnClose;
     187             :     BerkeleyEnvironment *env;
     188             : 
     189             : public:
     190             :     explicit BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode = "r+", bool fFlushOnCloseIn=true);
     191      443973 :     ~BerkeleyBatch() { Close(); }
     192             : 
     193             :     BerkeleyBatch(const BerkeleyBatch&) = delete;
     194             :     BerkeleyBatch& operator=(const BerkeleyBatch&) = delete;
     195             : 
     196             :     void Flush();
     197             :     void Close();
     198             :     static bool Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename);
     199             : 
     200             :     /* flush the wallet passively (TRY_LOCK)
     201             :        ideal to be called periodically */
     202             :     static bool PeriodicFlush(BerkeleyDatabase& database);
     203             :     /* verifies the database environment */
     204             :     static bool VerifyEnvironment(const fs::path& file_path, std::string& errorStr);
     205             :     /* verifies the database file */
     206             :     static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
     207             : 
     208             : public:
     209             :     template <typename K, typename T>
     210        7910 :     bool Read(const K& key, T& value)
     211             :     {
     212        7910 :         if (!pdb)
     213             :             return false;
     214             : 
     215             :         // Key
     216       15820 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     217        7910 :         ssKey.reserve(1000);
     218        7910 :         ssKey << key;
     219       15820 :         Dbt datKey(ssKey.data(), ssKey.size());
     220             : 
     221             :         // Read
     222        7910 :         Dbt datValue;
     223        7910 :         datValue.set_flags(DB_DBT_MALLOC);
     224        7910 :         int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
     225        7910 :         memory_cleanse(datKey.get_data(), datKey.get_size());
     226        7910 :         bool success = false;
     227        7910 :         if (datValue.get_data() != nullptr) {
     228             :             // Unserialize value
     229             :             try {
     230       15338 :                 CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION);
     231        7669 :                 ssValue >> value;
     232        7669 :                 success = true;
     233           0 :             } catch (const std::exception&) {
     234             :                 // In this case success remains 'false'
     235             :             }
     236             : 
     237             :             // Clear and free memory
     238        7669 :             memory_cleanse(datValue.get_data(), datValue.get_size());
     239        7669 :             free(datValue.get_data());
     240             :         }
     241        7910 :         return ret == 0 && success;
     242             :     }
     243             : 
     244             :     template <typename K, typename T>
     245      521851 :     bool Write(const K& key, const T& value, bool fOverwrite = true)
     246             :     {
     247      521851 :         if (!pdb)
     248             :             return true;
     249      521608 :         if (fReadOnly)
     250           0 :             assert(!"Write called on database in read-only mode");
     251             : 
     252             :         // Key
     253     1043459 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     254      521608 :         ssKey.reserve(1000);
     255      521608 :         ssKey << key;
     256     1043216 :         Dbt datKey(ssKey.data(), ssKey.size());
     257             : 
     258             :         // Value
     259     1043216 :         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
     260      521608 :         ssValue.reserve(10000);
     261      521608 :         ssValue << value;
     262     1043216 :         Dbt datValue(ssValue.data(), ssValue.size());
     263             : 
     264             :         // Write
     265      549441 :         int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
     266             : 
     267             :         // Clear memory in case it was a private key
     268      521608 :         memory_cleanse(datKey.get_data(), datKey.get_size());
     269      521608 :         memory_cleanse(datValue.get_data(), datValue.get_size());
     270      521608 :         return (ret == 0);
     271             :     }
     272             : 
     273             :     template <typename K>
     274        9281 :     bool Erase(const K& key)
     275             :     {
     276        9281 :         if (!pdb)
     277             :             return false;
     278        9281 :         if (fReadOnly)
     279           0 :             assert(!"Erase called on database in read-only mode");
     280             : 
     281             :         // Key
     282       18562 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     283        9281 :         ssKey.reserve(1000);
     284        9281 :         ssKey << key;
     285       18562 :         Dbt datKey(ssKey.data(), ssKey.size());
     286             : 
     287             :         // Erase
     288        9281 :         int ret = pdb->del(activeTxn, &datKey, 0);
     289             : 
     290             :         // Clear memory
     291        9281 :         memory_cleanse(datKey.get_data(), datKey.get_size());
     292        9281 :         return (ret == 0 || ret == DB_NOTFOUND);
     293             :     }
     294             : 
     295             :     template <typename K>
     296         403 :     bool Exists(const K& key)
     297             :     {
     298         403 :         if (!pdb)
     299             :             return false;
     300             : 
     301             :         // Key
     302         806 :         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
     303         403 :         ssKey.reserve(1000);
     304         403 :         ssKey << key;
     305         806 :         Dbt datKey(ssKey.data(), ssKey.size());
     306             : 
     307             :         // Exists
     308         403 :         int ret = pdb->exists(activeTxn, &datKey, 0);
     309             : 
     310             :         // Clear memory
     311         403 :         memory_cleanse(datKey.get_data(), datKey.get_size());
     312         403 :         return (ret == 0);
     313             :     }
     314             : 
     315         415 :     Dbc* GetCursor()
     316             :     {
     317         415 :         if (!pdb)
     318             :             return nullptr;
     319         415 :         Dbc* pcursor = nullptr;
     320         415 :         int ret = pdb->cursor(nullptr, &pcursor, 0);
     321         415 :         if (ret != 0)
     322             :             return nullptr;
     323         415 :         return pcursor;
     324             :     }
     325             : 
     326       32799 :     int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
     327             :     {
     328             :         // Read at cursor
     329       65598 :         Dbt datKey;
     330       32799 :         unsigned int fFlags = DB_NEXT;
     331       32799 :         if (setRange) {
     332           0 :             datKey.set_data(ssKey.data());
     333           0 :             datKey.set_size(ssKey.size());
     334           0 :             fFlags = DB_SET_RANGE;
     335             :         }
     336       65598 :         Dbt datValue;
     337       32799 :         datKey.set_flags(DB_DBT_MALLOC);
     338       32799 :         datValue.set_flags(DB_DBT_MALLOC);
     339       32799 :         int ret = pcursor->get(&datKey, &datValue, fFlags);
     340       32799 :         if (ret != 0)
     341             :             return ret;
     342       32384 :         else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
     343             :             return 99999;
     344             : 
     345             :         // Convert to streams
     346       32384 :         ssKey.SetType(SER_DISK);
     347       32384 :         ssKey.clear();
     348       32384 :         ssKey.write((char*)datKey.get_data(), datKey.get_size());
     349       32384 :         ssValue.SetType(SER_DISK);
     350       32384 :         ssValue.clear();
     351       32384 :         ssValue.write((char*)datValue.get_data(), datValue.get_size());
     352             : 
     353             :         // Clear and free memory
     354       32384 :         memory_cleanse(datKey.get_data(), datKey.get_size());
     355       32384 :         memory_cleanse(datValue.get_data(), datValue.get_size());
     356       32384 :         free(datKey.get_data());
     357       32384 :         free(datValue.get_data());
     358       32384 :         return 0;
     359             :     }
     360             : 
     361             : public:
     362         334 :     bool TxnBegin()
     363             :     {
     364         334 :         if (!pdb || activeTxn)
     365             :             return false;
     366         334 :         DbTxn* ptxn = env->TxnBegin();
     367         334 :         if (!ptxn)
     368           0 :             return false;
     369         334 :         activeTxn = ptxn;
     370         334 :         return true;
     371             :     }
     372             : 
     373         334 :     bool TxnCommit()
     374             :     {
     375         334 :         if (!pdb || !activeTxn)
     376             :             return false;
     377         334 :         int ret = activeTxn->commit(0);
     378         334 :         activeTxn = nullptr;
     379         334 :         return (ret == 0);
     380             :     }
     381             : 
     382           0 :     bool TxnAbort()
     383             :     {
     384           0 :         if (!pdb || !activeTxn)
     385             :             return false;
     386           0 :         int ret = activeTxn->abort();
     387           0 :         activeTxn = nullptr;
     388           0 :         return (ret == 0);
     389             :     }
     390             : 
     391           0 :     bool ReadVersion(int& nVersion)
     392             :     {
     393           0 :         nVersion = 0;
     394           0 :         return Read(std::string("version"), nVersion);
     395             :     }
     396             : 
     397         243 :     bool WriteVersion(int nVersion)
     398             :     {
     399         243 :         return Write(std::string("version"), nVersion);
     400             :     }
     401             : 
     402             :     bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr);
     403             : };
     404             : 
     405             : #endif // PIVX_WALLET_DB_H

Generated by: LCOV version 1.14