2012-09-03 21:14:03 +02:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2014-02-08 22:50:24 +01:00
|
|
|
// Copyright (c) 2009-2014 The Bitcoin developers
|
2012-09-03 21:14:03 +02:00
|
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2012-10-16 22:23:39 +02:00
|
|
|
#include "txdb.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
|
|
|
|
#include "core.h"
|
2014-03-10 16:46:53 +01:00
|
|
|
#include "pow.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
#include "uint256.h"
|
|
|
|
|
Split up util.cpp/h
Split up util.cpp/h into:
- string utilities (hex, base32, base64): no internal dependencies, no dependency on boost (apart from foreach)
- money utilities (parsesmoney, formatmoney)
- time utilities (gettime*, sleep, format date):
- and the rest (logging, argument parsing, config file parsing)
The latter is basically the environment and OS handling,
and is stripped of all utility functions, so we may want to
rename it to something else than util.cpp/h for clarity (Matt suggested
osinterface).
Breaks dependency of sha256.cpp on all the things pulled in by util.
2014-08-21 16:11:09 +02:00
|
|
|
#include <boost/thread.hpp>
|
2013-04-13 07:13:08 +02:00
|
|
|
#include <stdint.h>
|
2012-09-03 21:14:03 +02:00
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
|
|
|
|
if (coins.IsPruned())
|
|
|
|
batch.Erase(make_pair('c', hash));
|
|
|
|
else
|
|
|
|
batch.Write(make_pair('c', hash), coins);
|
|
|
|
}
|
|
|
|
|
|
|
|
void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) {
|
|
|
|
batch.Write('B', hash);
|
|
|
|
}
|
|
|
|
|
2013-01-28 21:05:26 +01:00
|
|
|
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) {
|
2012-09-03 21:14:03 +02:00
|
|
|
}
|
|
|
|
|
2014-07-19 16:42:48 +02:00
|
|
|
bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
|
2013-12-20 18:58:15 +01:00
|
|
|
return db.Read(make_pair('c', txid), coins);
|
2012-09-03 21:14:03 +02:00
|
|
|
}
|
|
|
|
|
2013-01-20 18:05:34 +01:00
|
|
|
bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) {
|
2012-09-03 21:14:03 +02:00
|
|
|
CLevelDBBatch batch;
|
|
|
|
BatchWriteCoins(batch, txid, coins);
|
|
|
|
return db.WriteBatch(batch);
|
|
|
|
}
|
|
|
|
|
2014-07-19 16:42:48 +02:00
|
|
|
bool CCoinsViewDB::HaveCoins(const uint256 &txid) const {
|
2013-12-20 18:58:15 +01:00
|
|
|
return db.Exists(make_pair('c', txid));
|
2012-09-03 21:14:03 +02:00
|
|
|
}
|
|
|
|
|
2014-07-19 16:42:48 +02:00
|
|
|
uint256 CCoinsViewDB::GetBestBlock() const {
|
2012-09-03 21:14:03 +02:00
|
|
|
uint256 hashBestChain;
|
|
|
|
if (!db.Read('B', hashBestChain))
|
2013-11-05 02:27:39 +01:00
|
|
|
return uint256(0);
|
|
|
|
return hashBestChain;
|
2012-09-03 21:14:03 +02:00
|
|
|
}
|
|
|
|
|
2013-11-05 02:27:39 +01:00
|
|
|
bool CCoinsViewDB::SetBestBlock(const uint256 &hashBlock) {
|
2012-09-03 21:14:03 +02:00
|
|
|
CLevelDBBatch batch;
|
2013-11-05 02:27:39 +01:00
|
|
|
BatchWriteHashBestChain(batch, hashBlock);
|
2012-09-03 21:14:03 +02:00
|
|
|
return db.WriteBatch(batch);
|
|
|
|
}
|
|
|
|
|
2014-08-24 02:08:05 +02:00
|
|
|
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
2013-09-18 12:38:08 +02:00
|
|
|
LogPrint("coindb", "Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size());
|
2012-09-03 21:14:03 +02:00
|
|
|
|
|
|
|
CLevelDBBatch batch;
|
2014-08-24 02:08:05 +02:00
|
|
|
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
2012-09-03 21:14:03 +02:00
|
|
|
BatchWriteCoins(batch, it->first, it->second);
|
2014-08-24 02:08:05 +02:00
|
|
|
CCoinsMap::iterator itOld = it++;
|
|
|
|
mapCoins.erase(itOld);
|
|
|
|
}
|
2013-11-05 02:27:39 +01:00
|
|
|
if (hashBlock != uint256(0))
|
|
|
|
BatchWriteHashBestChain(batch, hashBlock);
|
2012-09-03 21:14:03 +02:00
|
|
|
|
|
|
|
return db.WriteBatch(batch);
|
|
|
|
}
|
|
|
|
|
2013-11-06 02:58:43 +01:00
|
|
|
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
|
2012-09-04 18:12:00 +02:00
|
|
|
}
|
|
|
|
|
2012-09-03 21:14:03 +02:00
|
|
|
bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
|
|
|
|
{
|
|
|
|
return Write(make_pair('b', blockindex.GetBlockHash()), blockindex);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
|
|
|
|
return Write(make_pair('f', nFile), info);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
|
|
|
|
return Read(make_pair('f', nFile), info);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::WriteLastBlockFile(int nFile) {
|
|
|
|
return Write('l', nFile);
|
|
|
|
}
|
|
|
|
|
2012-10-21 21:23:13 +02:00
|
|
|
bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
|
|
|
|
if (fReindexing)
|
|
|
|
return Write('R', '1');
|
|
|
|
else
|
|
|
|
return Erase('R');
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
|
|
|
|
fReindexing = Exists('R');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-03 21:14:03 +02:00
|
|
|
bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
|
|
|
|
return Read('l', nFile);
|
|
|
|
}
|
|
|
|
|
2014-07-19 16:42:48 +02:00
|
|
|
bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
|
|
|
|
/* It seems that there are no "const iterators" for LevelDB. Since we
|
|
|
|
only need read operations on it, use a const-cast to get around
|
|
|
|
that restriction. */
|
|
|
|
leveldb::Iterator *pcursor = const_cast<CLevelDBWrapper*>(&db)->NewIterator();
|
2012-09-25 23:04:54 +02:00
|
|
|
pcursor->SeekToFirst();
|
|
|
|
|
2013-05-01 16:23:27 +02:00
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
2013-11-05 02:27:39 +01:00
|
|
|
stats.hashBlock = GetBestBlock();
|
2013-05-01 16:23:27 +02:00
|
|
|
ss << stats.hashBlock;
|
2013-04-13 07:13:08 +02:00
|
|
|
int64_t nTotalAmount = 0;
|
2012-09-25 23:04:54 +02:00
|
|
|
while (pcursor->Valid()) {
|
2013-03-09 18:02:57 +01:00
|
|
|
boost::this_thread::interruption_point();
|
2012-09-25 23:04:54 +02:00
|
|
|
try {
|
|
|
|
leveldb::Slice slKey = pcursor->key();
|
|
|
|
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
|
|
|
|
char chType;
|
|
|
|
ssKey >> chType;
|
2013-03-09 18:02:57 +01:00
|
|
|
if (chType == 'c') {
|
2012-09-25 23:04:54 +02:00
|
|
|
leveldb::Slice slValue = pcursor->value();
|
|
|
|
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
|
|
|
|
CCoins coins;
|
|
|
|
ssValue >> coins;
|
|
|
|
uint256 txhash;
|
|
|
|
ssKey >> txhash;
|
2013-05-01 16:23:27 +02:00
|
|
|
ss << txhash;
|
|
|
|
ss << VARINT(coins.nVersion);
|
2013-12-20 18:58:15 +01:00
|
|
|
ss << (coins.fCoinBase ? 'c' : 'n');
|
2013-05-01 16:23:27 +02:00
|
|
|
ss << VARINT(coins.nHeight);
|
2012-09-25 23:04:54 +02:00
|
|
|
stats.nTransactions++;
|
2013-05-01 16:23:27 +02:00
|
|
|
for (unsigned int i=0; i<coins.vout.size(); i++) {
|
|
|
|
const CTxOut &out = coins.vout[i];
|
|
|
|
if (!out.IsNull()) {
|
2012-09-25 23:04:54 +02:00
|
|
|
stats.nTransactionOutputs++;
|
2013-05-01 16:23:27 +02:00
|
|
|
ss << VARINT(i+1);
|
|
|
|
ss << out;
|
|
|
|
nTotalAmount += out.nValue;
|
|
|
|
}
|
2012-09-25 23:04:54 +02:00
|
|
|
}
|
|
|
|
stats.nSerializedSize += 32 + slValue.size();
|
2013-05-01 16:23:27 +02:00
|
|
|
ss << VARINT(0);
|
2012-09-25 23:04:54 +02:00
|
|
|
}
|
|
|
|
pcursor->Next();
|
|
|
|
} catch (std::exception &e) {
|
2014-04-30 14:45:24 +02:00
|
|
|
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
|
2012-09-25 23:04:54 +02:00
|
|
|
}
|
|
|
|
}
|
2013-11-05 02:27:39 +01:00
|
|
|
stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight;
|
2013-05-01 16:23:27 +02:00
|
|
|
stats.hashSerialized = ss.GetHash();
|
|
|
|
stats.nTotalAmount = nTotalAmount;
|
2012-09-25 23:04:54 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-01-11 01:47:57 +01:00
|
|
|
bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
|
|
|
|
return Read(make_pair('t', txid), pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) {
|
|
|
|
CLevelDBBatch batch;
|
|
|
|
for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
|
|
|
|
batch.Write(make_pair('t', it->first), it->second);
|
|
|
|
return WriteBatch(batch);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
|
|
|
|
return Write(std::make_pair('F', name), fValue ? '1' : '0');
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
|
|
|
char ch;
|
|
|
|
if (!Read(std::make_pair('F', name), ch))
|
|
|
|
return false;
|
|
|
|
fValue = ch == '1';
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-03 21:14:03 +02:00
|
|
|
bool CBlockTreeDB::LoadBlockIndexGuts()
|
|
|
|
{
|
2014-08-07 23:58:50 +02:00
|
|
|
boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
|
2012-09-03 21:14:03 +02:00
|
|
|
|
|
|
|
CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
|
|
|
|
ssKeySet << make_pair('b', uint256(0));
|
|
|
|
pcursor->Seek(ssKeySet.str());
|
|
|
|
|
|
|
|
// Load mapBlockIndex
|
|
|
|
while (pcursor->Valid()) {
|
2013-03-09 18:02:57 +01:00
|
|
|
boost::this_thread::interruption_point();
|
2012-09-03 21:14:03 +02:00
|
|
|
try {
|
|
|
|
leveldb::Slice slKey = pcursor->key();
|
|
|
|
CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
|
|
|
|
char chType;
|
|
|
|
ssKey >> chType;
|
2013-03-09 18:02:57 +01:00
|
|
|
if (chType == 'b') {
|
2012-09-03 21:14:03 +02:00
|
|
|
leveldb::Slice slValue = pcursor->value();
|
|
|
|
CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
|
|
|
|
CDiskBlockIndex diskindex;
|
|
|
|
ssValue >> diskindex;
|
|
|
|
|
|
|
|
// Construct block index object
|
|
|
|
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
|
|
|
|
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
|
|
|
|
pindexNew->nHeight = diskindex.nHeight;
|
|
|
|
pindexNew->nFile = diskindex.nFile;
|
|
|
|
pindexNew->nDataPos = diskindex.nDataPos;
|
|
|
|
pindexNew->nUndoPos = diskindex.nUndoPos;
|
|
|
|
pindexNew->nVersion = diskindex.nVersion;
|
|
|
|
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
|
|
|
|
pindexNew->nTime = diskindex.nTime;
|
|
|
|
pindexNew->nBits = diskindex.nBits;
|
|
|
|
pindexNew->nNonce = diskindex.nNonce;
|
|
|
|
pindexNew->nStatus = diskindex.nStatus;
|
|
|
|
pindexNew->nTx = diskindex.nTx;
|
|
|
|
|
2014-03-10 16:46:53 +01:00
|
|
|
if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits))
|
|
|
|
return error("LoadBlockIndex() : CheckProofOfWork failed: %s", pindexNew->ToString());
|
2012-09-03 21:14:03 +02:00
|
|
|
|
|
|
|
pcursor->Next();
|
|
|
|
} else {
|
|
|
|
break; // if shutdown requested or finished loading block index
|
|
|
|
}
|
|
|
|
} catch (std::exception &e) {
|
2014-04-30 14:45:24 +02:00
|
|
|
return error("%s : Deserialize or I/O error - %s", __func__, e.what());
|
2012-09-03 21:14:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|