// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2012 The Bitcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file license.txt or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_DB_H #define BITCOIN_DB_H #include "main.h" #include #include #include #include class CAddress; class CAddrMan; class CBlockLocator; class CDiskBlockIndex; class CDiskTxPos; class CMasterKey; class COutPoint; class CTxIndex; class CWallet; class CWalletTx; extern unsigned int nWalletDBUpdated; void ThreadFlushWalletDB(void* parg); bool BackupWallet(const CWallet& wallet, const std::string& strDest); class CDBEnv { private: bool fDetachDB; bool fDbEnvInit; boost::filesystem::path pathEnv; void EnvShutdown(); public: mutable CCriticalSection cs_db; DbEnv dbenv; CDBEnv(); ~CDBEnv(); bool Open(boost::filesystem::path pathEnv_); void Close(); void Flush(bool fShutdown); void CheckpointLSN(std::string strFile); void SetDetach(bool fDetachDB_) { fDetachDB = fDetachDB_; } }; extern CDBEnv bitdb; /** RAII class that provides access to a Berkeley database */ class CDB { protected: Db* pdb; std::string strFile; std::vector vTxn; bool fReadOnly; explicit CDB(const char* pszFile, const char* pszMode="r+"); ~CDB() { Close(); } public: void Close(); private: CDB(const CDB&); void operator=(const CDB&); protected: template bool Read(const K& key, T& value) { if (!pdb) return false; // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Read Dbt datValue; datValue.set_flags(DB_DBT_MALLOC); int ret = pdb->get(GetTxn(), &datKey, &datValue, 0); memset(datKey.get_data(), 0, datKey.get_size()); if (datValue.get_data() == NULL) return false; // Unserialize value CDataStream ssValue((char*)datValue.get_data(), (char*)datValue.get_data() + datValue.get_size(), SER_DISK, CLIENT_VERSION); ssValue >> value; // Clear and free memory memset(datValue.get_data(), 0, datValue.get_size()); free(datValue.get_data()); return (ret == 0); } template bool Write(const K& key, const T& value, bool fOverwrite=true) { if (!pdb) return false; if (fReadOnly) assert(!"Write called on database in read-only mode"); // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Value CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; Dbt datValue(&ssValue[0], ssValue.size()); // Write int ret = pdb->put(GetTxn(), &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE)); // Clear memory in case it was a private key memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); return (ret == 0); } template bool Erase(const K& key) { if (!pdb) return false; if (fReadOnly) assert(!"Erase called on database in read-only mode"); // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Erase int ret = pdb->del(GetTxn(), &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0 || ret == DB_NOTFOUND); } template bool Exists(const K& key) { if (!pdb) return false; // Key CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Exists int ret = pdb->exists(GetTxn(), &datKey, 0); // Clear memory memset(datKey.get_data(), 0, datKey.get_size()); return (ret == 0); } Dbc* GetCursor() { if (!pdb) return NULL; Dbc* pcursor = NULL; int ret = pdb->cursor(NULL, &pcursor, 0); if (ret != 0) return NULL; return pcursor; } int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, unsigned int fFlags=DB_NEXT) { // Read at cursor Dbt datKey; if (fFlags == DB_SET || fFlags == DB_SET_RANGE || fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { datKey.set_data(&ssKey[0]); datKey.set_size(ssKey.size()); } Dbt datValue; if (fFlags == DB_GET_BOTH || fFlags == DB_GET_BOTH_RANGE) { datValue.set_data(&ssValue[0]); datValue.set_size(ssValue.size()); } datKey.set_flags(DB_DBT_MALLOC); datValue.set_flags(DB_DBT_MALLOC); int ret = pcursor->get(&datKey, &datValue, fFlags); if (ret != 0) return ret; else if (datKey.get_data() == NULL || datValue.get_data() == NULL) return 99999; // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); ssKey.write((char*)datKey.get_data(), datKey.get_size()); ssValue.SetType(SER_DISK); ssValue.clear(); ssValue.write((char*)datValue.get_data(), datValue.get_size()); // Clear and free memory memset(datKey.get_data(), 0, datKey.get_size()); memset(datValue.get_data(), 0, datValue.get_size()); free(datKey.get_data()); free(datValue.get_data()); return 0; } DbTxn* GetTxn() { if (!vTxn.empty()) return vTxn.back(); else return NULL; } public: bool TxnBegin() { if (!pdb) return false; DbTxn* ptxn = NULL; int ret = bitdb.dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_WRITE_NOSYNC); if (!ptxn || ret != 0) return false; vTxn.push_back(ptxn); return true; } bool TxnCommit() { if (!pdb) return false; if (vTxn.empty()) return false; int ret = vTxn.back()->commit(0); vTxn.pop_back(); return (ret == 0); } bool TxnAbort() { if (!pdb) return false; if (vTxn.empty()) return false; int ret = vTxn.back()->abort(); vTxn.pop_back(); return (ret == 0); } bool ReadVersion(int& nVersion) { nVersion = 0; return Read(std::string("version"), nVersion); } bool WriteVersion(int nVersion) { return Write(std::string("version"), nVersion); } bool static Rewrite(const std::string& strFile, const char* pszSkip = NULL); }; /** Access to the transaction database (blkindex.dat) */ class CTxDB : public CDB { public: CTxDB(const char* pszMode="r+") : CDB("blkindex.dat", pszMode) { } private: CTxDB(const CTxDB&); void operator=(const CTxDB&); public: bool ReadTxIndex(uint256 hash, CTxIndex& txindex); bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex); bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight); bool EraseTxIndex(const CTransaction& tx); bool ContainsTx(uint256 hash); bool ReadOwnerTxes(uint160 hash160, int nHeight, std::vector& vtx); bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex); bool ReadDiskTx(uint256 hash, CTransaction& tx); bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex); bool ReadDiskTx(COutPoint outpoint, CTransaction& tx); bool WriteBlockIndex(const CDiskBlockIndex& blockindex); bool ReadHashBestChain(uint256& hashBestChain); bool WriteHashBestChain(uint256 hashBestChain); bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); bool LoadBlockIndex(); }; /** Access to the (IP) address database (peers.dat) */ class CAddrDB { private: boost::filesystem::path pathAddr; public: CAddrDB(); bool Write(const CAddrMan& addr); bool Read(CAddrMan& addr); }; #endif // BITCOIN_DB_H