// Copyright (c) 2009-2012 The Bitcoin Developers. // Authored by Google, Inc. // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_LEVELDB_H #define BITCOIN_LEVELDB_H #include "main.h" #include #include #include #include #include // Class that provides access to a LevelDB. Note that this class is frequently // instantiated on the stack and then destroyed again, so instantiation has to // be very cheap. Unfortunately that means, a CTxDB instance is actually just a // wrapper around some global state. // // A LevelDB is a key/value store that is optimized for fast usage on hard // disks. It prefers long read/writes to seeks and is based on a series of // sorted key/value mapping files that are stacked on top of each other, with // newer files overriding older files. A background thread compacts them // together when too many files stack up. // // Learn more: http://code.google.com/p/leveldb/ class CTxDB { public: CTxDB(const char* pszMode="r+"); ~CTxDB() { // Note that this is not the same as Close() because it deletes only // data scoped to this TxDB object. delete activeBatch; } // Destroys the underlying shared global state accessed by this TxDB. void Close(); private: leveldb::DB *pdb; // Points to the global instance. // A batch stores up writes and deletes for atomic application. When this // field is non-NULL, writes/deletes go there instead of directly to disk. leveldb::WriteBatch *activeBatch; leveldb::Options options; bool fReadOnly; int nVersion; protected: // Returns true and sets (value,false) if activeBatch contains the given key // or leaves value alone and sets deleted = true if activeBatch contains a // delete for it. bool ScanBatch(const CDataStream &key, std::string *value, bool *deleted) const; template bool Read(const K& key, T& value) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; std::string strValue; bool readFromDb = true; if (activeBatch) { // First we must search for it in the currently pending set of // changes to the db. If not found in the batch, go on to read disk. bool deleted = false; readFromDb = ScanBatch(ssKey, &strValue, &deleted) == false; if (deleted) { return false; } } if (readFromDb) { leveldb::Status status = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &strValue); if (!status.ok()) { if (status.IsNotFound()) return false; // Some unexpected error. printf("LevelDB read failure: %s\n", status.ToString().c_str()); return false; } } // Unserialize value try { CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION); ssValue >> value; } catch (const std::exception&) { return false; } return true; } template bool Write(const K& key, const T& value) { if (fReadOnly) assert(!"Write called on database in read-only mode"); CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; CDataStream ssValue(SER_DISK, CLIENT_VERSION); ssValue.reserve(10000); ssValue << value; if (activeBatch) { activeBatch->Put(ssKey.str(), ssValue.str()); return true; } leveldb::Status status = pdb->Put(leveldb::WriteOptions(), ssKey.str(), ssValue.str()); if (!status.ok()) { printf("LevelDB write failure: %s\n", status.ToString().c_str()); return false; } return true; } template bool Erase(const K& key) { if (!pdb) return false; if (fReadOnly) assert(!"Erase called on database in read-only mode"); CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; if (activeBatch) { activeBatch->Delete(ssKey.str()); return true; } leveldb::Status status = pdb->Delete(leveldb::WriteOptions(), ssKey.str()); return (status.ok() || status.IsNotFound()); } template bool Exists(const K& key) { CDataStream ssKey(SER_DISK, CLIENT_VERSION); ssKey.reserve(1000); ssKey << key; std::string unused; if (activeBatch) { bool deleted; if (ScanBatch(ssKey, &unused, &deleted) && !deleted) { return true; } } leveldb::Status status = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &unused); return status.IsNotFound() == false; } public: bool TxnBegin(); bool TxnCommit(); bool TxnAbort() { delete activeBatch; activeBatch = NULL; return true; } bool ReadVersion(int& nVersion) { nVersion = 0; return Read(std::string("version"), nVersion); } bool WriteVersion(int nVersion) { return Write(std::string("version"), nVersion); } 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 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 ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust); bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust); bool ReadSyncCheckpoint(uint256& hashCheckpoint); bool WriteSyncCheckpoint(uint256 hashCheckpoint); bool ReadCheckpointPubKey(std::string& strPubKey); bool WriteCheckpointPubKey(const std::string& strPubKey); bool ReadModifierUpgradeTime(unsigned int& nUpgradeTime); bool WriteModifierUpgradeTime(const unsigned int& nUpgradeTime); bool LoadBlockIndex(); }; #endif // BITCOIN_DB_H