// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2011 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 "key.h" #include #include #include #include class CTxIndex; class CDiskBlockIndex; class CDiskTxPos; class COutPoint; class CAddress; class CWalletTx; class CWallet; class CAccount; class CAccountingEntry; class CBlockLocator; extern unsigned int nWalletDBUpdated; extern DbEnv dbenv; extern void DBFlush(bool fShutdown); void ThreadFlushWalletDB(void* parg); bool BackupWallet(const CWallet& wallet, const std::string& strDest); 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); 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); 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); ssKey.reserve(1000); ssKey << key; Dbt datKey(&ssKey[0], ssKey.size()); // Value CDataStream ssValue(SER_DISK); 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); 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); 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 = dbenv.txn_begin(GetTxn(), &ptxn, DB_TXN_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); }; 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 EraseBlockIndex(uint256 hash); bool ReadHashBestChain(uint256& hashBestChain); bool WriteHashBestChain(uint256 hashBestChain); bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); bool LoadBlockIndex(); }; class CAddrDB : public CDB { public: CAddrDB(const char* pszMode="r+") : CDB("addr.dat", pszMode) { } private: CAddrDB(const CAddrDB&); void operator=(const CAddrDB&); public: bool WriteAddress(const CAddress& addr); bool EraseAddress(const CAddress& addr); bool LoadAddresses(); }; bool LoadAddresses(); class CKeyPool { public: int64 nTime; std::vector vchPubKey; CKeyPool() { nTime = GetTime(); } CKeyPool(const std::vector& vchPubKeyIn) { nTime = GetTime(); vchPubKey = vchPubKeyIn; } IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) READWRITE(nVersion); READWRITE(nTime); READWRITE(vchPubKey); ) }; enum DBErrors { DB_LOAD_OK, DB_CORRUPT, DB_TOO_NEW, DB_LOAD_FAIL, DB_NEED_REWRITE }; class CWalletDB : public CDB { public: CWalletDB(std::string strFilename, const char* pszMode="r+") : CDB(strFilename.c_str(), pszMode) { } private: CWalletDB(const CWalletDB&); void operator=(const CWalletDB&); public: bool ReadName(const std::string& strAddress, std::string& strName) { strName = ""; return Read(std::make_pair(std::string("name"), strAddress), strName); } bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); bool ReadTx(uint256 hash, CWalletTx& wtx) { return Read(std::make_pair(std::string("tx"), hash), wtx); } bool WriteTx(uint256 hash, const CWalletTx& wtx) { nWalletDBUpdated++; return Write(std::make_pair(std::string("tx"), hash), wtx); } bool EraseTx(uint256 hash) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("tx"), hash)); } bool ReadKey(const std::vector& vchPubKey, CPrivKey& vchPrivKey) { vchPrivKey.clear(); return Read(std::make_pair(std::string("key"), vchPubKey), vchPrivKey); } bool WriteKey(const std::vector& vchPubKey, const CPrivKey& vchPrivKey) { nWalletDBUpdated++; return Write(std::make_pair(std::string("key"), vchPubKey), vchPrivKey, false); } bool WriteCryptedKey(const std::vector& vchPubKey, const std::vector& vchCryptedSecret, bool fEraseUnencryptedKey = true) { nWalletDBUpdated++; if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) return false; if (fEraseUnencryptedKey) { Erase(std::make_pair(std::string("key"), vchPubKey)); Erase(std::make_pair(std::string("wkey"), vchPubKey)); } return true; } bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) { nWalletDBUpdated++; return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); } bool WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; return Write(std::string("bestblock"), locator); } bool ReadBestBlock(CBlockLocator& locator) { return Read(std::string("bestblock"), locator); } bool ReadDefaultKey(std::vector& vchPubKey) { vchPubKey.clear(); return Read(std::string("defaultkey"), vchPubKey); } bool WriteDefaultKey(const std::vector& vchPubKey) { nWalletDBUpdated++; return Write(std::string("defaultkey"), vchPubKey); } bool ReadPool(int64 nPool, CKeyPool& keypool) { return Read(std::make_pair(std::string("pool"), nPool), keypool); } bool WritePool(int64 nPool, const CKeyPool& keypool) { nWalletDBUpdated++; return Write(std::make_pair(std::string("pool"), nPool), keypool); } bool ErasePool(int64 nPool) { nWalletDBUpdated++; return Erase(std::make_pair(std::string("pool"), nPool)); } template bool ReadSetting(const std::string& strKey, T& value) { return Read(std::make_pair(std::string("setting"), strKey), value); } template bool WriteSetting(const std::string& strKey, const T& value) { nWalletDBUpdated++; return Write(std::make_pair(std::string("setting"), strKey), value); } bool ReadAccount(const std::string& strAccount, CAccount& account); bool WriteAccount(const std::string& strAccount, const CAccount& account); bool WriteAccountingEntry(const CAccountingEntry& acentry); int64 GetAccountCreditDebit(const std::string& strAccount); void ListAccountCreditDebit(const std::string& strAccount, std::list& acentries); int LoadWallet(CWallet* pwallet); }; #endif