src/txdb-leveldb.h
// 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 <map>
#include <string>
#include <vector>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
#include "ringsig.h"
/*
prefixes
ao
ki
version
tx
bidx
bhdx
hashBestChain
hashBestHeaderChain
bnBestInvalidTrust
hashSyncCheckpoint
strCheckpointPubKey
old:
blockindex
*/
// 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.
if (activeBatch)
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<typename K, typename T>
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.
LogPrintf("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 (std::exception &e)
{
return false;
}
return true;
}
template<typename K, typename T>
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())
{
LogPrintf("LevelDB write failure: %s\n", status.ToString().c_str());
return false;
};
return true;
}
template<typename K>
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<typename K>
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;
}
leveldb::DB* GetInstance()
{
return pdb;
}
bool ReadVersion(int& nVersion)
{
nVersion = 0;
return Read(std::string("version"), nVersion);
}
bool WriteVersion(int nVersion)
{
return Write(std::string("version"), nVersion);
}
int CheckVersion();
int RecreateDB();
bool WriteKeyImage(ec_point& keyImage, CKeyImageSpent& keyImageSpent);
bool ReadKeyImage(ec_point& keyImage, CKeyImageSpent& keyImageSpent);
bool EraseKeyImage(ec_point& keyImage);
bool WriteAnonOutput(CPubKey& pkCoin, CAnonOutput& ao);
bool ReadAnonOutput(CPubKey& pkCoin, CAnonOutput& ao);
bool EraseAnonOutput(CPubKey& pkCoin);
bool EraseRange(const std::string &sPrefix, uint32_t &nAffected);
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 EraseBlockIndex(const uint256& blockhash);
bool WriteBlockThinIndex(const CDiskBlockThinIndex& blockindex);
bool ReadBlockThinIndex(const uint256& hash, CDiskBlockThinIndex& blockindex);
bool ReadHashBestChain(uint256& hashBestChain);
bool WriteHashBestChain(uint256 hashBestChain);
bool ReadHashBestHeaderChain(uint256& hashBestChain);
bool WriteHashBestHeaderChain(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 LoadBlockIndex();
bool LoadBlockThinIndex();
private:
bool LoadBlockIndexGuts();
};
#endif // BITCOIN_DB_H