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" +
|
||||
" -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" +
|
||||
" -txindex " + _("Maintain a full transaction index (default: 0)") + "\n" +
|
||||
" -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + "\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" +
|
||||
|
@ -781,7 +782,7 @@ bool AppInit2()
|
|||
if (nTotalCache < (1 << 22))
|
||||
nTotalCache = (1 << 22); // total cache cannot be less than 4 MiB
|
||||
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
|
||||
nTotalCache -= nBlockTreeDBCache;
|
||||
size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache
|
||||
|
@ -806,6 +807,9 @@ bool AppInit2()
|
|||
if (!VerifyDB())
|
||||
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
|
||||
// requested to kill bitcoin-qt during the last operation. If so, exit.
|
||||
// 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 fReindex = false;
|
||||
bool fBenchmark = false;
|
||||
bool fTxIndex = false;
|
||||
unsigned int nCoinCacheSize = 5000;
|
||||
|
||||
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
|
||||
int nHeight = -1;
|
||||
{
|
||||
|
@ -1641,6 +1661,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||
int64 nFees = 0;
|
||||
int nInputs = 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++)
|
||||
{
|
||||
|
||||
|
@ -1680,6 +1703,8 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||
if (!tx.IsCoinBase())
|
||||
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;
|
||||
if (fBenchmark)
|
||||
|
@ -1719,6 +1744,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust
|
|||
return error("ConnectBlock() : WriteBlockIndex failed");
|
||||
}
|
||||
|
||||
if (fTxIndex)
|
||||
pblocktree->WriteTxIndex(vPos);
|
||||
|
||||
// add this block to the view's block chain
|
||||
if (!view.SetBestBlock(pindex))
|
||||
return false;
|
||||
|
@ -2548,6 +2576,10 @@ bool static LoadBlockIndexDB()
|
|||
pblocktree->ReadReindexing(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
|
||||
pindexBest = pcoinsTip->GetBestBlock();
|
||||
if (pindexBest == NULL)
|
||||
|
@ -2652,13 +2684,10 @@ bool LoadBlockIndex()
|
|||
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
||||
}
|
||||
|
||||
if (fReindex)
|
||||
return true;
|
||||
|
||||
//
|
||||
// Load block index from databases
|
||||
//
|
||||
if (!LoadBlockIndexDB())
|
||||
if (!fReindex && !LoadBlockIndexDB())
|
||||
return false;
|
||||
|
||||
//
|
||||
|
@ -2666,6 +2695,13 @@ bool LoadBlockIndex()
|
|||
//
|
||||
if (mapBlockIndex.empty())
|
||||
{
|
||||
fTxIndex = GetBoolArg("-txindex", false);
|
||||
pblocktree->WriteFlag("txindex", fTxIndex);
|
||||
printf("Initializing databases...\n");
|
||||
|
||||
if (fReindex)
|
||||
return true;
|
||||
|
||||
// Genesis Block:
|
||||
// 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)
|
||||
|
|
24
src/main.h
24
src/main.h
|
@ -93,6 +93,7 @@ extern bool fImporting;
|
|||
extern bool fReindex;
|
||||
extern bool fBenchmark;
|
||||
extern int nScriptCheckThreads;
|
||||
extern bool fTxIndex;
|
||||
extern unsigned int nCoinCacheSize;
|
||||
|
||||
// Settings
|
||||
|
@ -196,9 +197,8 @@ static inline std::string BlockHashStr(const uint256& hash)
|
|||
|
||||
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
|
||||
|
||||
class CDiskBlockPos
|
||||
struct CDiskBlockPos
|
||||
{
|
||||
public:
|
||||
int nFile;
|
||||
unsigned int nPos;
|
||||
|
||||
|
@ -228,7 +228,27 @@ public:
|
|||
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 */
|
||||
|
|
23
src/txdb.cpp
23
src/txdb.cpp
|
@ -145,6 +145,29 @@ bool CCoinsViewDB::GetStats(CCoinsStats &stats) {
|
|||
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()
|
||||
{
|
||||
leveldb::Iterator *pcursor = NewIterator();
|
||||
|
|
|
@ -43,6 +43,10 @@ public:
|
|||
bool WriteLastBlockFile(int nFile);
|
||||
bool WriteReindexing(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();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue