Browse Source

Add optional transaction index to databases

By specifying -txindex when initializing the database, a txid-to-diskpos
index is maintained in the blktree database. This database is used to
help answering getrawtransaction() RPC queries, when enabled.

Changing the -txindex value requires a -reindex; the client will abort
at startup if the database and the specified -txindex mismatch.
tags/v0.15.1
Pieter Wuille 8 years ago
parent
commit
2d1fa42e85
5 changed files with 94 additions and 7 deletions
  1. 5
    1
      src/init.cpp
  2. 40
    4
      src/main.cpp
  3. 22
    2
      src/main.h
  4. 23
    0
      src/txdb.cpp
  5. 4
    0
      src/txdb.h

+ 5
- 1
src/init.cpp View File

@@ -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.

+ 40
- 4
src/main.cpp View File

@@ -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
@@ -949,6 +950,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;
{
@@ -1632,6 +1652,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++)
{

@@ -1671,6 +1694,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)
@@ -1710,6 +1735,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;
@@ -2554,6 +2582,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)
@@ -2658,13 +2690,10 @@ bool LoadBlockIndex()
hashGenesisBlock = uint256("000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
}

if (fReindex)
return true;

//
// Load block index from databases
//
if (!LoadBlockIndexDB())
if (!fReindex && !LoadBlockIndexDB())
return false;

//
@@ -2672,6 +2701,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)

+ 22
- 2
src/main.h View File

@@ -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
- 0
src/txdb.cpp View File

@@ -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();

+ 4
- 0
src/txdb.h View File

@@ -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…
Cancel
Save