Merge pull request #2168 from sipa/txindex
Add optional transaction index to databases
This commit is contained in:
commit
63cc7661a5
5 changed files with 94 additions and 7 deletions
|
@ -305,6 +305,7 @@ std::string HelpMessage()
|
||||||
" -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" +
|
" -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" +
|
||||||
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
|
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
|
||||||
" -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n" +
|
" -checklevel=<n> " + _("How thorough the block verification is (0-4, default: 3)") + "\n" +
|
||||||
|
" -txindex " + _("Maintain a full transaction index (default: 0)") + "\n" +
|
||||||
" -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n" +
|
" -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\n" +
|
||||||
" -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" +
|
" -reindex " + _("Rebuild blockchain index from current blk000??.dat files") + "\n" +
|
||||||
" -par=N " + _("Set the number of script verification threads (1-16, 0=auto, default: 0)") + "\n" +
|
" -par=N " + _("Set the number of script verification threads (1-16, 0=auto, default: 0)") + "\n" +
|
||||||
|
@ -781,7 +782,7 @@ bool AppInit2()
|
||||||
if (nTotalCache < (1 << 22))
|
if (nTotalCache < (1 << 22))
|
||||||
nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB
|
nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB
|
||||||
size_t nBlockTreeDBCache = nTotalCache / 8;
|
size_t nBlockTreeDBCache = nTotalCache / 8;
|
||||||
if (nBlockTreeDBCache > (1 << 21))
|
if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false))
|
||||||
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB
|
||||||
nTotalCache -= nBlockTreeDBCache;
|
nTotalCache -= nBlockTreeDBCache;
|
||||||
size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
|
size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
|
||||||
|
@ -806,6 +807,9 @@ bool AppInit2()
|
||||||
if (!VerifyDB())
|
if (!VerifyDB())
|
||||||
return InitError(_("Corrupted block database detected. Please restart the client with -reindex."));
|
return InitError(_("Corrupted block database detected. Please restart the client with -reindex."));
|
||||||
|
|
||||||
|
if (mapArgs.count("-txindex") && fTxIndex != GetBoolArg("-txindex", false))
|
||||||
|
return InitError(_("You need to rebuild the databases using -reindex to change -txindex"));
|
||||||
|
|
||||||
// as LoadBlockIndex can take several minutes, it's possible the user
|
// as LoadBlockIndex can take several minutes, it's possible the user
|
||||||
// requested to kill bitcoin-qt during the last operation. If so, exit.
|
// requested to kill bitcoin-qt during the last operation. If so, exit.
|
||||||
// As the program has not fully started yet, Shutdown() is possibly overkill.
|
// As the program has not fully started yet, Shutdown() is possibly overkill.
|
||||||
|
|
44
src/main.cpp
44
src/main.cpp
|
@ -45,6 +45,7 @@ int nScriptCheckThreads = 0;
|
||||||
bool fImporting = false;
|
bool fImporting = false;
|
||||||
bool fReindex = false;
|
bool fReindex = false;
|
||||||
bool fBenchmark = false;
|
bool fBenchmark = false;
|
||||||
|
bool fTxIndex = false;
|
||||||
unsigned int nCoinCacheSize = 5000;
|
unsigned int nCoinCacheSize = 5000;
|
||||||
|
|
||||||
CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have
|
CMedianFilter<int> cPeerBlockCounts(8, 0); // Amount of blocks that other nodes claim to have
|
||||||
|
@ -950,6 +951,25 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fTxIndex) {
|
||||||
|
CDiskTxPos postx;
|
||||||
|
if (pblocktree->ReadTxIndex(hash, postx)) {
|
||||||
|
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
||||||
|
CBlockHeader header;
|
||||||
|
try {
|
||||||
|
file >> header;
|
||||||
|
fseek(file, postx.nTxOffset, SEEK_CUR);
|
||||||
|
file >> txOut;
|
||||||
|
} catch (std::exception &e) {
|
||||||
|
return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__);
|
||||||
|
}
|
||||||
|
hashBlock = header.GetHash();
|
||||||
|
if (txOut.GetHash() != hash)
|
||||||
|
return error("%s() : txid mismatch", __PRETTY_FUNCTION__);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||||
int nHeight = -1;
|
int nHeight = -1;
|
||||||
{
|
{
|
||||||
|
@ -1641,6 +1661,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||||
int64 nFees = 0;
|
int64 nFees = 0;
|
||||||
int nInputs = 0;
|
int nInputs = 0;
|
||||||
unsigned int nSigOps = 0;
|
unsigned int nSigOps = 0;
|
||||||
|
CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(vtx.size()));
|
||||||
|
std::vector<std::pair<uint256, CDiskTxPos> > vPos;
|
||||||
|
vPos.reserve(vtx.size());
|
||||||
for (unsigned int i=0; i<vtx.size(); i++)
|
for (unsigned int i=0; i<vtx.size(); i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1680,6 +1703,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
blockundo.vtxundo.push_back(txundo);
|
blockundo.vtxundo.push_back(txundo);
|
||||||
|
|
||||||
|
vPos.push_back(std::make_pair(GetTxHash(i), pos));
|
||||||
|
pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||||
}
|
}
|
||||||
int64 nTime = GetTimeMicros() - nStart;
|
int64 nTime = GetTimeMicros() - nStart;
|
||||||
if (fBenchmark)
|
if (fBenchmark)
|
||||||
|
@ -1719,6 +1744,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
||||||
return error("ConnectBlock() : WriteBlockIndex failed");
|
return error("ConnectBlock() : WriteBlockIndex failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fTxIndex)
|
||||||
|
pblocktree->WriteTxIndex(vPos);
|
||||||
|
|
||||||
// add this block to the view's block chain
|
// add this block to the view's block chain
|
||||||
if (!view.SetBestBlock(pindex))
|
if (!view.SetBestBlock(pindex))
|
||||||
return false;
|
return false;
|
||||||
|
@ -2548,6 +2576,10 @@ bool static LoadBlockIndexDB()
|
||||||
pblocktree->ReadReindexing(fReindexing);
|
pblocktree->ReadReindexing(fReindexing);
|
||||||
fReindex |= fReindexing;
|
fReindex |= fReindexing;
|
||||||
|
|
||||||
|
// Check whether we have a transaction index
|
||||||
|
pblocktree->ReadFlag("txindex", fTxIndex);
|
||||||
|
printf("LoadBlockIndex(): transaction index %s\n", fTxIndex ? "enabled" : "disabled");
|
||||||
|
|
||||||
// Load hashBestChain pointer to end of best chain
|
// Load hashBestChain pointer to end of best chain
|
||||||
pindexBest = pcoinsTip->GetBestBlock();
|
pindexBest = pcoinsTip->GetBestBlock();
|
||||||
if (pindexBest == NULL)
|
if (pindexBest == NULL)
|
||||||
|
@ -2652,13 +2684,10 @@ bool LoadBlockIndex()
|
||||||
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fReindex)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Load block index from databases
|
// Load block index from databases
|
||||||
//
|
//
|
||||||
if (!LoadBlockIndexDB())
|
if (!fReindex && !LoadBlockIndexDB())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -2666,6 +2695,13 @@ bool LoadBlockIndex()
|
||||||
//
|
//
|
||||||
if (mapBlockIndex.empty())
|
if (mapBlockIndex.empty())
|
||||||
{
|
{
|
||||||
|
fTxIndex = GetBoolArg("-txindex", false);
|
||||||
|
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||||
|
printf("Initializing databases...\n");
|
||||||
|
|
||||||
|
if (fReindex)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Genesis Block:
|
// Genesis Block:
|
||||||
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
|
// CBlock(hash=000000000019d6, ver=1, hashPrevBlock=00000000000000, hashMerkleRoot=4a5e1e, nTime=1231006505, nBits=1d00ffff, nNonce=2083236893, vtx=1)
|
||||||
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
|
// CTransaction(hash=4a5e1e, ver=1, vin.size=1, vout.size=1, nLockTime=0)
|
||||||
|
|
24
src/main.h
24
src/main.h
|
@ -93,6 +93,7 @@ extern bool fImporting;
|
||||||
extern bool fReindex;
|
extern bool fReindex;
|
||||||
extern bool fBenchmark;
|
extern bool fBenchmark;
|
||||||
extern int nScriptCheckThreads;
|
extern int nScriptCheckThreads;
|
||||||
|
extern bool fTxIndex;
|
||||||
extern unsigned int nCoinCacheSize;
|
extern unsigned int nCoinCacheSize;
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
|
@ -196,9 +197,8 @@ static inline std::string BlockHashStr(const uint256& hash)
|
||||||
|
|
||||||
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
|
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
|
||||||
|
|
||||||
class CDiskBlockPos
|
struct CDiskBlockPos
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
int nFile;
|
int nFile;
|
||||||
unsigned int nPos;
|
unsigned int nPos;
|
||||||
|
|
||||||
|
@ -228,7 +228,27 @@ public:
|
||||||
bool IsNull() const { return (nFile == -1); }
|
bool IsNull() const { return (nFile == -1); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CDiskTxPos : public CDiskBlockPos
|
||||||
|
{
|
||||||
|
unsigned int nTxOffset; // after header
|
||||||
|
|
||||||
|
IMPLEMENT_SERIALIZE(
|
||||||
|
READWRITE(*(CDiskBlockPos*)this);
|
||||||
|
READWRITE(VARINT(nTxOffset));
|
||||||
|
)
|
||||||
|
|
||||||
|
CDiskTxPos(const CDiskBlockPos &blockIn, unsigned int nTxOffsetIn) : CDiskBlockPos(blockIn.nFile, blockIn.nPos), nTxOffset(nTxOffsetIn) {
|
||||||
|
}
|
||||||
|
|
||||||
|
CDiskTxPos() {
|
||||||
|
SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNull() {
|
||||||
|
CDiskBlockPos::SetNull();
|
||||||
|
nTxOffset = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/** An inpoint - a combination of a transaction and an index n into its vin */
|
/** An inpoint - a combination of a transaction and an index n into its vin */
|
||||||
|
|
23
src/txdb.cpp
23
src/txdb.cpp
|
@ -145,6 +145,29 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
bool CBlockTreeDB::LoadBlockIndexGuts()
|
bool CBlockTreeDB::LoadBlockIndexGuts()
|
||||||
{
|
{
|
||||||
leveldb::Iterator *pcursor = NewIterator();
|
leveldb::Iterator *pcursor = NewIterator();
|
||||||
|
|
|
@ -43,6 +43,10 @@ public:
|
||||||
bool WriteLastBlockFile(int nFile);
|
bool WriteLastBlockFile(int nFile);
|
||||||
bool WriteReindexing(bool fReindex);
|
bool WriteReindexing(bool fReindex);
|
||||||
bool ReadReindexing(bool &fReindex);
|
bool ReadReindexing(bool &fReindex);
|
||||||
|
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos);
|
||||||
|
bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list);
|
||||||
|
bool WriteFlag(const std::string &name, bool fValue);
|
||||||
|
bool ReadFlag(const std::string &name, bool &fValue);
|
||||||
bool LoadBlockIndexGuts();
|
bool LoadBlockIndexGuts();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue