dash/src/dbwrapper.h

737 lines
21 KiB
C
Raw Normal View History

// Copyright (c) 2012-2015 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_DBWRAPPER_H
#define BITCOIN_DBWRAPPER_H
#include "clientversion.h"
#include "serialize.h"
#include "streams.h"
#include "util.h"
#include "utilstrencodings.h"
#include "version.h"
#include <typeindex>
#include <boost/filesystem/path.hpp>
#include <leveldb/db.h>
#include <leveldb/write_batch.h>
static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
class dbwrapper_error : public std::runtime_error
2013-01-29 01:44:19 +01:00
{
public:
dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
2013-01-29 01:44:19 +01:00
};
class CDBWrapper;
/** These should be considered an implementation detail of the specific database.
*/
namespace dbwrapper_private {
/** Handle database error by throwing dbwrapper_error exception.
*/
void HandleError(const leveldb::Status& status);
/** Work around circular dependency, as well as for testing in dbwrapper_tests.
* Database obfuscation should be considered an implementation detail of the
* specific database.
*/
const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
};
2013-01-29 01:44:19 +01:00
/** Batch of changes queued to be written to a CDBWrapper */
class CDBBatch
{
friend class CDBWrapper;
private:
const CDBWrapper &parent;
leveldb::WriteBatch batch;
CDataStream ssKey;
CDataStream ssValue;
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
size_t size_estimate;
public:
/**
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
* @param[in] parent CDBWrapper that this batch is to be submitted to
*/
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { };
void Clear()
{
batch.Clear();
size_estimate = 0;
}
template <typename K, typename V>
void Write(const K& key, const V& value)
{
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
Write(ssKey, value);
ssKey.clear();
}
template <typename V>
void Write(const CDataStream& _ssKey, const V& value)
{
leveldb::Slice slKey(_ssKey.data(), _ssKey.size());
ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
ssValue << value;
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
leveldb::Slice slValue(ssValue.data(), ssValue.size());
batch.Put(slKey, slValue);
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
// - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
// - byte[]: key
// - varint: value length
// - byte[]: value
// The formula below assumes the key and value are both less than 16k.
size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
ssValue.clear();
}
template <typename K>
void Erase(const K& key)
{
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
Erase(ssKey);
ssKey.clear();
}
void Erase(const CDataStream& _ssKey) {
leveldb::Slice slKey(_ssKey.data(), _ssKey.size());
batch.Delete(slKey);
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
// - byte: header
// - varint: key length
// - byte[]: key
// The formula below assumes the key is less than 16kB.
size_estimate += 2 + (slKey.size() > 127) + slKey.size();
}
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
size_t SizeEstimate() const { return size_estimate; }
};
class CDBIterator
{
private:
const CDBWrapper &parent;
leveldb::Iterator *piter;
public:
2015-10-08 09:44:10 +02:00
/**
* @param[in] _parent Parent CDBWrapper instance.
* @param[in] _piter The original leveldb iterator.
2015-10-08 09:44:10 +02:00
*/
CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
parent(_parent), piter(_piter) { };
~CDBIterator();
bool Valid();
void SeekToFirst();
template<typename K> void Seek(const K& key) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
Seek(ssKey);
}
void Seek(const CDataStream& ssKey) {
leveldb::Slice slKey(ssKey.data(), ssKey.size());
piter->Seek(slKey);
}
void Next();
template<typename K> bool GetKey(K& key) {
try {
CDataStream ssKey = GetKey();
ssKey >> key;
} catch (const std::exception&) {
return false;
}
return true;
}
CDataStream GetKey() {
leveldb::Slice slKey = piter->key();
return CDataStream(slKey.data(), slKey.data() + slKey.size(), SER_DISK, CLIENT_VERSION);
}
unsigned int GetKeySize() {
return piter->key().size();
}
template<typename V> bool GetValue(V& value) {
leveldb::Slice slValue = piter->value();
try {
CDataStream ssValue(slValue.data(), slValue.data() + slValue.size(), SER_DISK, CLIENT_VERSION);
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
ssValue >> value;
} catch (const std::exception&) {
return false;
}
return true;
}
unsigned int GetValueSize() {
return piter->value().size();
}
};
class CDBWrapper
{
friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
private:
//! custom environment this database is using (may be NULL in case of default environment)
leveldb::Env* penv;
//! database options used
leveldb::Options options;
//! options used when reading from the database
leveldb::ReadOptions readoptions;
//! options used when iterating over values of the database
leveldb::ReadOptions iteroptions;
//! options used when writing to the database
leveldb::WriteOptions writeoptions;
//! options used when sync writing to the database
leveldb::WriteOptions syncoptions;
//! the database itself
leveldb::DB* pdb;
//! a key used for optional XOR-obfuscation of the database
std::vector<unsigned char> obfuscate_key;
//! the key under which the obfuscation key is stored
static const std::string OBFUSCATE_KEY_KEY;
//! the length of the obfuscate key in number of bytes
static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
std::vector<unsigned char> CreateObfuscateKey() const;
public:
/**
* @param[in] path Location in the filesystem where leveldb data will be stored.
* @param[in] nCacheSize Configures various leveldb cache settings.
* @param[in] fMemory If true, use leveldb's memory environment.
* @param[in] fWipe If true, remove all existing data.
* @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
* with a zero'd byte array.
*/
CDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
~CDBWrapper();
template <typename K>
bool ReadDataStream(const K& key, CDataStream& ssValue) const
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
return ReadDataStream(ssKey, ssValue);
}
bool ReadDataStream(const CDataStream& ssKey, CDataStream& ssValue) const
{
leveldb::Slice slKey(ssKey.data(), ssKey.size());
std::string strValue;
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
if (!status.ok()) {
if (status.IsNotFound())
return false;
2014-05-21 12:50:46 +02:00
LogPrintf("LevelDB read failure: %s\n", status.ToString());
dbwrapper_private::HandleError(status);
}
CDataStream ssValueTmp(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
ssValueTmp.Xor(obfuscate_key);
ssValue = std::move(ssValueTmp);
return true;
}
template <typename K, typename V>
bool Read(const K& key, V& value) const
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
return Read(ssKey, value);
}
template <typename V>
bool Read(const CDataStream& ssKey, V& value) const
{
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
if (!ReadDataStream(ssKey, ssValue)) {
return false;
}
try {
ssValue >> value;
} catch (const std::exception&) {
return false;
}
return true;
}
template <typename K, typename V>
bool Write(const K& key, const V& value, bool fSync = false)
{
CDBBatch batch(*this);
batch.Write(key, value);
return WriteBatch(batch, fSync);
}
template <typename K>
bool Exists(const K& key) const
{
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
return Exists(ssKey);
}
bool Exists(const CDataStream& key) const
{
leveldb::Slice slKey(key.data(), key.size());
std::string strValue;
leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
if (!status.ok()) {
if (status.IsNotFound())
return false;
2014-05-21 12:50:46 +02:00
LogPrintf("LevelDB read failure: %s\n", status.ToString());
dbwrapper_private::HandleError(status);
}
return true;
}
template <typename K>
bool Erase(const K& key, bool fSync = false)
{
CDBBatch batch(*this);
batch.Erase(key);
return WriteBatch(batch, fSync);
}
bool WriteBatch(CDBBatch& batch, bool fSync = false);
// not available for LevelDB; provide for compatibility with BDB
bool Flush()
{
return true;
}
bool Sync()
{
CDBBatch batch(*this);
return WriteBatch(batch, true);
}
CDBIterator *NewIterator()
{
return new CDBIterator(*this, pdb->NewIterator(iteroptions));
2015-10-08 09:44:10 +02:00
}
/**
* Return true if the database managed by this class contains no entries.
*/
bool IsEmpty();
Merge #10195: Switch chainstate db and cache to per-txout model 589827975 scripted-diff: various renames for per-utxo consistency (Pieter Wuille) a5e02bc7f Increase travis unit test timeout (Pieter Wuille) 73de2c1ff Rename CCoinsCacheEntry::coins to coin (Pieter Wuille) 119e552f7 Merge CCoinsViewCache's GetOutputFor and AccessCoin (Pieter Wuille) 580b02309 [MOVEONLY] Move old CCoins class to txdb.cpp (Pieter Wuille) 8b25d2c0c Upgrade from per-tx database to per-txout (Pieter Wuille) b2af357f3 Reduce reserved memory space for flushing (Pieter Wuille) 41aa5b79a Pack Coin more tightly (Pieter Wuille) 97072d668 Remove unused CCoins methods (Pieter Wuille) ce23efaa5 Extend coins_tests (Pieter Wuille) 508307968 Switch CCoinsView and chainstate db from per-txid to per-txout (Pieter Wuille) 4ec0d9e79 Refactor GetUTXOStats in preparation for per-COutPoint iteration (Pieter Wuille) 13870b56f Replace CCoins-based CTxMemPool::pruneSpent with isSpent (Pieter Wuille) 05293f3cb Remove ModifyCoins/ModifyNewCoins (Pieter Wuille) 961e48397 Switch tests from ModifyCoins to AddCoin/SpendCoin (Pieter Wuille) 8b3868c1b Switch CScriptCheck to use Coin instead of CCoins (Pieter Wuille) c87b957a3 Only pass things committed to by tx's witness hash to CScriptCheck (Matt Corallo) f68cdfe92 Switch from per-tx to per-txout CCoinsViewCache methods in some places (Pieter Wuille) 000391132 Introduce new per-txout CCoinsViewCache functions (Pieter Wuille) bd83111a0 Optimization: Coin&& to ApplyTxInUndo (Pieter Wuille) cb2c7fdac Replace CTxInUndo with Coin (Pieter Wuille) 422634e2f Introduce Coin, a single unspent output (Pieter Wuille) 7d991b55d Store/allow tx metadata in all undo records (Pieter Wuille) c3aa0c119 Report on-disk size in gettxoutsetinfo (Pieter Wuille) d34242430 Remove/ignore tx version in utxo and undo (Pieter Wuille) 7e0032290 Add specialization of SipHash for 256 + 32 bit data (Pieter Wuille) e484652fc Introduce CHashVerifier to hash read data (Pieter Wuille) f54580e7e error() in disconnect for disk corruption, not inconsistency (Pieter Wuille) e66dbde6d Add SizeEstimate to CDBBatch (Pieter Wuille) Tree-SHA512: ce1fb1e40c77d38915cd02189fab7a8b125c7f44d425c85579d872c3bede3a437760997907c99d7b3017ced1c2de54b2ac7223d99d83a6658fe5ef61edef1de3
2017-06-02 00:47:58 +02:00
template<typename K>
size_t EstimateSize(const K& key_begin, const K& key_end) const
{
CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey1 << key_begin;
ssKey2 << key_end;
leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
uint64_t size = 0;
leveldb::Range range(slKey1, slKey2);
pdb->GetApproximateSizes(&range, 1, &size);
return size;
}
/**
* Compact a certain range of keys in the database.
*/
template<typename K>
void CompactRange(const K& key_begin, const K& key_end) const
{
CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey1 << key_begin;
ssKey2 << key_end;
leveldb::Slice slKey1(ssKey1.data(), ssKey1.size());
leveldb::Slice slKey2(ssKey2.data(), ssKey2.size());
pdb->CompactRange(&slKey1, &slKey2);
}
};
template<typename CDBTransaction>
class CDBTransactionIterator
{
private:
CDBTransaction& transaction;
typedef typename std::remove_pointer<decltype(transaction.parent.NewIterator())>::type ParentIterator;
// We maintain 2 iterators, one for the transaction and one for the parent
// At all times, only one of both provides the current value. The decision is made by comparing the current keys
// of both iterators, so that always the smaller key is the current one. On Next(), the previously chosen iterator
// is advanced.
typename CDBTransaction::WritesMap::iterator transactionIt;
std::unique_ptr<ParentIterator> parentIt;
CDataStream parentKey;
bool curIsParent{false};
public:
CDBTransactionIterator(CDBTransaction& _transaction) :
transaction(_transaction),
parentKey(SER_DISK, CLIENT_VERSION)
{
transactionIt = transaction.writes.end();
parentIt = std::unique_ptr<ParentIterator>(transaction.parent.NewIterator());
}
void SeekToFirst() {
transactionIt = transaction.writes.begin();
parentIt->SeekToFirst();
SkipDeletedAndOverwritten();
DecideCur();
}
template<typename K>
void Seek(const K& key) {
Seek(CDBTransaction::KeyToDataStream(key));
}
void Seek(const CDataStream& ssKey) {
transactionIt = transaction.writes.lower_bound(ssKey);
parentIt->Seek(ssKey);
SkipDeletedAndOverwritten();
DecideCur();
}
bool Valid() {
return transactionIt != transaction.writes.end() || parentIt->Valid();
}
void Next() {
if (transactionIt == transaction.writes.end() && !parentIt->Valid()) {
return;
}
if (curIsParent) {
assert(parentIt->Valid());
parentIt->Next();
SkipDeletedAndOverwritten();
} else {
assert(transactionIt != transaction.writes.end());
++transactionIt;
}
DecideCur();
}
template<typename K>
bool GetKey(K& key) {
if (!Valid()) {
return false;
}
if (curIsParent) {
try {
// TODO try to avoid this copy (we need a stream that allows reading from external buffers)
CDataStream ssKey = parentKey;
ssKey >> key;
} catch (const std::exception&) {
return false;
}
return true;
} else {
try {
// TODO try to avoid this copy (we need a stream that allows reading from external buffers)
CDataStream ssKey = transactionIt->first;
ssKey >> key;
} catch (const std::exception&) {
return false;
}
return true;
}
}
CDataStream GetKey() {
if (!Valid()) {
return CDataStream(SER_DISK, CLIENT_VERSION);
}
if (curIsParent) {
return parentKey;
} else {
return transactionIt->first;
}
}
unsigned int GetKeySize() {
if (!Valid()) {
return 0;
}
if (curIsParent) {
return parentIt->GetKeySize();
} else {
return transactionIt->first.vKey.size();
}
}
template<typename V>
bool GetValue(V& value) {
if (!Valid()) {
return false;
}
if (curIsParent) {
return transaction.Read(parentKey, value);
} else {
return transaction.Read(transactionIt->first, value);
}
};
private:
void SkipDeletedAndOverwritten() {
while (parentIt->Valid()) {
parentKey = parentIt->GetKey();
if (!transaction.deletes.count(parentKey) && !transaction.writes.count(parentKey)) {
break;
}
parentIt->Next();
}
}
void DecideCur() {
if (transactionIt != transaction.writes.end() && !parentIt->Valid()) {
curIsParent = false;
} else if (transactionIt == transaction.writes.end() && parentIt->Valid()) {
curIsParent = true;
} else if (transactionIt != transaction.writes.end() && parentIt->Valid()) {
if (CDBTransaction::DataStreamCmp::less(transactionIt->first, parentKey)) {
curIsParent = false;
} else {
curIsParent = true;
}
}
}
};
template<typename Parent, typename CommitTarget>
class CDBTransaction {
friend class CDBTransactionIterator<CDBTransaction>;
protected:
Parent &parent;
CommitTarget &commitTarget;
struct DataStreamCmp {
static bool less(const CDataStream& a, const CDataStream& b) {
return std::lexicographical_compare(
(const uint8_t*)a.data(), (const uint8_t*)a.data() + a.size(),
(const uint8_t*)b.data(), (const uint8_t*)b.data() + b.size());
}
bool operator()(const CDataStream& a, const CDataStream& b) const {
return less(a, b);
}
};
struct ValueHolder {
virtual ~ValueHolder() = default;
virtual void Write(const CDataStream& ssKey, CommitTarget &parent) = 0;
};
typedef std::unique_ptr<ValueHolder> ValueHolderPtr;
template <typename V>
struct ValueHolderImpl : ValueHolder {
ValueHolderImpl(const V &_value) : value(_value) { }
virtual void Write(const CDataStream& ssKey, CommitTarget &commitTarget) {
// we're moving the value instead of copying it. This means that Write() can only be called once per
// ValueHolderImpl instance. Commit() clears the write maps, so this ok.
commitTarget.Write(ssKey, std::move(value));
}
V value;
};
template<typename K>
static CDataStream KeyToDataStream(const K& key) {
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
ssKey << key;
return ssKey;
}
typedef std::map<CDataStream, ValueHolderPtr, DataStreamCmp> WritesMap;
typedef std::set<CDataStream, DataStreamCmp> DeletesSet;
WritesMap writes;
DeletesSet deletes;
public:
CDBTransaction(Parent &_parent, CommitTarget &_commitTarget) : parent(_parent), commitTarget(_commitTarget) {}
template <typename K, typename V>
void Write(const K& key, const V& v) {
Write(KeyToDataStream(key), v);
}
template <typename V>
void Write(const CDataStream& ssKey, const V& v) {
deletes.erase(ssKey);
auto it = writes.emplace(ssKey, nullptr).first;
it->second = std::make_unique<ValueHolderImpl<V>>(v);
}
template <typename K, typename V>
bool Read(const K& key, V& value) {
return Read(KeyToDataStream(key), value);
}
template <typename V>
bool Read(const CDataStream& ssKey, V& value) {
if (deletes.count(ssKey)) {
return false;
}
auto it = writes.find(ssKey);
if (it != writes.end()) {
auto *impl = dynamic_cast<ValueHolderImpl<V> *>(it->second.get());
if (!impl) {
throw std::runtime_error("Read called with V != previously written type");
}
value = impl->value;
return true;
}
return parent.Read(ssKey, value);
}
template <typename K>
bool Exists(const K& key) {
return Exists(KeyToDataStream(key));
}
bool Exists(const CDataStream& ssKey) {
if (deletes.count(ssKey)) {
return false;
}
if (writes.count(ssKey)) {
return true;
}
return parent.Exists(ssKey);
}
template <typename K>
void Erase(const K& key) {
return Erase(KeyToDataStream(key));
}
void Erase(const CDataStream& ssKey) {
writes.erase(ssKey);
deletes.emplace(ssKey);
}
void Clear() {
writes.clear();
deletes.clear();
}
void Commit() {
for (const auto &k : deletes) {
commitTarget.Erase(k);
}
for (auto &p : writes) {
p.second->Write(p.first, commitTarget);
}
Clear();
}
bool IsClean() {
return writes.empty() && deletes.empty();
}
CDBTransactionIterator<CDBTransaction>* NewIterator() {
return new CDBTransactionIterator<CDBTransaction>(*this);
}
std::unique_ptr<CDBTransactionIterator<CDBTransaction>> NewIteratorUniquePtr() {
return std::make_unique<CDBTransactionIterator<CDBTransaction>>(*this);
}
};
template<typename Parent, typename CommitTarget>
class CScopedDBTransaction {
public:
typedef CDBTransaction<Parent, CommitTarget> Transaction;
private:
Transaction &dbTransaction;
std::function<void ()> commitHandler;
std::function<void ()> rollbackHandler;
bool didCommitOrRollback{};
public:
CScopedDBTransaction(Transaction &dbTx) : dbTransaction(dbTx) {}
~CScopedDBTransaction() {
if (!didCommitOrRollback)
Rollback();
}
void Commit() {
assert(!didCommitOrRollback);
didCommitOrRollback = true;
dbTransaction.Commit();
if (commitHandler)
commitHandler();
}
void Rollback() {
assert(!didCommitOrRollback);
didCommitOrRollback = true;
dbTransaction.Clear();
if (rollbackHandler)
rollbackHandler();
}
static std::unique_ptr<CScopedDBTransaction<Parent, CommitTarget>> Begin(Transaction &dbTx) {
assert(dbTx.IsClean());
return std::make_unique<CScopedDBTransaction<Parent, CommitTarget>>(dbTx);
}
void SetCommitHandler(const std::function<void ()> &h) {
commitHandler = h;
}
void SetRollbackHandler(const std::function<void ()> &h) {
rollbackHandler = h;
}
};
#endif // BITCOIN_DBWRAPPER_H