Browse Source

Make FindBlockByHeight constant-time.

Remove the pnext pointer in CBlockIndex, and replace it with a
vBlockIndexByHeight vector (no effect on memory usage). pnext can
now be replaced by vBlockIndexByHeight[nHeight+1], but
FindBlockByHeight becomes constant-time.

This also means the entire mapBlockIndex structure and the block
index entries in it become purely blocktree-related data, and
independent from the currently active chain, potentially allowing
them to be protected by separate mutexes in the future.
miguelfreitas
Pieter Wuille 12 years ago
parent
commit
0fe8010a10
  1. 2
      contrib/test-patches/bitcoind-comparison.patch
  2. 54
      src/main.cpp
  3. 24
      src/main.h
  4. 5
      src/rpcblockchain.cpp
  5. 2
      src/wallet.cpp

2
contrib/test-patches/bitcoind-comparison.patch

@ -3,9 +3,9 @@ index 04a8618..519429a 100644
--- a/src/main.cpp --- a/src/main.cpp
+++ b/src/main.cpp +++ b/src/main.cpp
@@ -31,8 +31,8 @@ CTxMemPool mempool; @@ -31,8 +31,8 @@ CTxMemPool mempool;
unsigned int nTransactionsUpdated = 0;
map<uint256, CBlockIndex*> mapBlockIndex; map<uint256, CBlockIndex*> mapBlockIndex;
std::vector<CBlockIndex*> vBlockIndexByHeight;
-uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); -uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
-static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); -static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
+uint256 hashGenesisBlock("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"); +uint256 hashGenesisBlock("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206");

54
src/main.cpp

@ -31,6 +31,7 @@ CTxMemPool mempool;
unsigned int nTransactionsUpdated = 0; unsigned int nTransactionsUpdated = 0;
map<uint256, CBlockIndex*> mapBlockIndex; map<uint256, CBlockIndex*> mapBlockIndex;
std::vector<CBlockIndex*> vBlockIndexByHeight;
uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); uint256 hashGenesisBlock("0x000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32); static CBigNum bnProofOfWorkLimit(~uint256(0) >> 32);
CBlockIndex* pindexGenesisBlock = NULL; CBlockIndex* pindexGenesisBlock = NULL;
@ -1036,19 +1037,9 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock
static CBlockIndex* pblockindexFBBHLast; static CBlockIndex* pblockindexFBBHLast;
CBlockIndex* FindBlockByHeight(int nHeight) CBlockIndex* FindBlockByHeight(int nHeight)
{ {
CBlockIndex *pblockindex; if (nHeight >= (int)vBlockIndexByHeight.size())
if (nHeight < nBestHeight / 2) return NULL;
pblockindex = pindexGenesisBlock; return vBlockIndexByHeight[nHeight];
else
pblockindex = pindexBest;
if (pblockindexFBBHLast && abs(nHeight - pblockindex->nHeight) > abs(nHeight - pblockindexFBBHLast->nHeight))
pblockindex = pblockindexFBBHLast;
while (pblockindex->nHeight > nHeight)
pblockindex = pblockindex->pprev;
while (pblockindex->nHeight < nHeight)
pblockindex = pblockindex->pnext;
pblockindexFBBHLast = pblockindex;
return pblockindex;
} }
bool CBlock::ReadFromDisk(const CBlockIndex* pindex) bool CBlock::ReadFromDisk(const CBlockIndex* pindex)
@ -1231,7 +1222,7 @@ void static InvalidBlockFound(CBlockIndex *pindex) {
pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex)); pblocktree->WriteBlockIndex(CDiskBlockIndex(pindex));
setBlockIndexValid.erase(pindex); setBlockIndexValid.erase(pindex);
InvalidChainFound(pindex); InvalidChainFound(pindex);
if (pindex->pnext) { if (pindex->GetNextInMainChain()) {
CValidationState stateDummy; CValidationState stateDummy;
ConnectBestBlock(stateDummy); // reorganise away from the failed block ConnectBestBlock(stateDummy); // reorganise away from the failed block
} }
@ -1271,7 +1262,7 @@ bool ConnectBestBlock(CValidationState &state) {
if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork) if (pindexBest == NULL || pindexTest->nChainWork > pindexBest->nChainWork)
vAttach.push_back(pindexTest); vAttach.push_back(pindexTest);
if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { if (pindexTest->pprev == NULL || pindexTest->GetNextInMainChain()) {
reverse(vAttach.begin(), vAttach.end()); reverse(vAttach.begin(), vAttach.end());
BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) { BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
@ -1849,15 +1840,10 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
// At this point, all changes have been done to the database. // At this point, all changes have been done to the database.
// Proceed by updating the memory structures. // Proceed by updating the memory structures.
// Disconnect shorter branch // Register new best chain
BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) vBlockIndexByHeight.resize(pindexNew->nHeight + 1);
if (pindex->pprev)
pindex->pprev->pnext = NULL;
// Connect longer branch
BOOST_FOREACH(CBlockIndex* pindex, vConnect) BOOST_FOREACH(CBlockIndex* pindex, vConnect)
if (pindex->pprev) vBlockIndexByHeight[pindex->nHeight] = pindex;
pindex->pprev->pnext = pindex;
// Resurrect memory transactions that were in the disconnected branch // Resurrect memory transactions that were in the disconnected branch
BOOST_FOREACH(CTransaction& tx, vResurrect) { BOOST_FOREACH(CTransaction& tx, vResurrect) {
@ -2609,12 +2595,12 @@ bool static LoadBlockIndexDB()
nBestHeight = pindexBest->nHeight; nBestHeight = pindexBest->nHeight;
nBestChainWork = pindexBest->nChainWork; nBestChainWork = pindexBest->nChainWork;
// set 'next' pointers in best chain // register best chain
CBlockIndex *pindex = pindexBest; CBlockIndex *pindex = pindexBest;
while(pindex != NULL && pindex->pprev != NULL) { vBlockIndexByHeight.resize(pindexBest->nHeight + 1);
CBlockIndex *pindexPrev = pindex->pprev; while(pindex != NULL) {
pindexPrev->pnext = pindex; vBlockIndexByHeight[pindex->nHeight] = pindex;
pindex = pindexPrev; pindex = pindex->pprev;
} }
printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n", printf("LoadBlockIndexDB(): hashBestChain=%s height=%d date=%s\n",
hashBestChain.ToString().c_str(), nBestHeight, hashBestChain.ToString().c_str(), nBestHeight,
@ -2683,7 +2669,7 @@ bool VerifyDB() {
CBlockIndex *pindex = pindexState; CBlockIndex *pindex = pindexState;
while (pindex != pindexBest) { while (pindex != pindexBest) {
boost::this_thread::interruption_point(); boost::this_thread::interruption_point();
pindex = pindex->pnext; pindex = pindex->GetNextInMainChain();
CBlock block; CBlock block;
if (!block.ReadFromDisk(pindex)) if (!block.ReadFromDisk(pindex))
return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
@ -2859,7 +2845,7 @@ void PrintBlockTree()
vector<CBlockIndex*>& vNext = mapNext[pindex]; vector<CBlockIndex*>& vNext = mapNext[pindex];
for (unsigned int i = 0; i < vNext.size(); i++) for (unsigned int i = 0; i < vNext.size(); i++)
{ {
if (vNext[i]->pnext) if (vNext[i]->GetNextInMainChain())
{ {
swap(vNext[0], vNext[i]); swap(vNext[0], vNext[i]);
break; break;
@ -3440,10 +3426,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Send the rest of the chain // Send the rest of the chain
if (pindex) if (pindex)
pindex = pindex->pnext; pindex = pindex->GetNextInMainChain();
int nLimit = 500; int nLimit = 500;
printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit); printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str(), nLimit);
for (; pindex; pindex = pindex->pnext) for (; pindex; pindex = pindex->GetNextInMainChain())
{ {
if (pindex->GetBlockHash() == hashStop) if (pindex->GetBlockHash() == hashStop)
{ {
@ -3483,14 +3469,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
// Find the last block the caller has in the main chain // Find the last block the caller has in the main chain
pindex = locator.GetBlockIndex(); pindex = locator.GetBlockIndex();
if (pindex) if (pindex)
pindex = pindex->pnext; pindex = pindex->GetNextInMainChain();
} }
// we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end // we must use CBlocks, as CBlockHeaders won't include the 0x00 nTx count at the end
vector<CBlock> vHeaders; vector<CBlock> vHeaders;
int nLimit = 2000; int nLimit = 2000;
printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str()); printf("getheaders %d to %s\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().c_str());
for (; pindex; pindex = pindex->pnext) for (; pindex; pindex = pindex->GetNextInMainChain())
{ {
vHeaders.push_back(pindex->GetBlockHeader()); vHeaders.push_back(pindex->GetBlockHeader());
if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop)

24
src/main.h

@ -69,6 +69,7 @@ extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main; extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex; extern std::map<uint256, CBlockIndex*> mapBlockIndex;
extern std::vector<CBlockIndex*> vBlockIndexByHeight;
extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid; extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid;
extern uint256 hashGenesisBlock; extern uint256 hashGenesisBlock;
extern CBlockIndex* pindexGenesisBlock; extern CBlockIndex* pindexGenesisBlock;
@ -1589,10 +1590,8 @@ enum BlockStatus {
/** The block chain is a tree shaped structure starting with the /** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple * genesis block at the root, with each block potentially having multiple
* candidates to be the next block. pprev and pnext link a path through the * candidates to be the next block. A blockindex may have multiple pprev pointing
* main/longest chain. A blockindex may have multiple pprev pointing back * to it, but at most one of them can be part of the currently active branch.
* to it, but pnext will only point forward to the longest branch, or will
* be null if the block is not part of the longest chain.
*/ */
class CBlockIndex class CBlockIndex
{ {
@ -1603,9 +1602,6 @@ public:
// pointer to the index of the predecessor of this block // pointer to the index of the predecessor of this block
CBlockIndex* pprev; CBlockIndex* pprev;
// (memory only) pointer to the index of the *active* successor of this block
CBlockIndex* pnext;
// height of the entry in the chain. The genesis block has height 0 // height of the entry in the chain. The genesis block has height 0
int nHeight; int nHeight;
@ -1643,7 +1639,6 @@ public:
{ {
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pnext = NULL;
nHeight = 0; nHeight = 0;
nFile = 0; nFile = 0;
nDataPos = 0; nDataPos = 0;
@ -1664,7 +1659,6 @@ public:
{ {
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pnext = NULL;
nHeight = 0; nHeight = 0;
nFile = 0; nFile = 0;
nDataPos = 0; nDataPos = 0;
@ -1733,7 +1727,11 @@ public:
bool IsInMainChain() const bool IsInMainChain() const
{ {
return (pnext || this == pindexBest); return nHeight < (int)vBlockIndexByHeight.size() && vBlockIndexByHeight[nHeight] == this;
}
CBlockIndex *GetNextInMainChain() const {
return nHeight+1 >= (int)vBlockIndexByHeight.size() ? NULL : vBlockIndexByHeight[nHeight+1];
} }
bool CheckIndex() const bool CheckIndex() const
@ -1762,9 +1760,9 @@ public:
const CBlockIndex* pindex = this; const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan/2; i++) for (int i = 0; i < nMedianTimeSpan/2; i++)
{ {
if (!pindex->pnext) if (!pindex->GetNextInMainChain())
return GetBlockTime(); return GetBlockTime();
pindex = pindex->pnext; pindex = pindex->GetNextInMainChain();
} }
return pindex->GetMedianTimePast(); return pindex->GetMedianTimePast();
} }
@ -1779,7 +1777,7 @@ public:
std::string ToString() const std::string ToString() const
{ {
return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)", return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, pnext, nHeight, pprev, GetNextInMainChain(), nHeight,
hashMerkleRoot.ToString().c_str(), hashMerkleRoot.ToString().c_str(),
GetBlockHash().ToString().c_str()); GetBlockHash().ToString().c_str());
} }

5
src/rpcblockchain.cpp

@ -65,8 +65,9 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex)
if (blockindex->pprev) if (blockindex->pprev)
result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()));
if (blockindex->pnext) CBlockIndex *pnext = blockindex->GetNextInMainChain();
result.push_back(Pair("nextblockhash", blockindex->pnext->GetBlockHash().GetHex())); if (pnext)
result.push_back(Pair("nextblockhash", pnext->GetBlockHash().GetHex()));
return result; return result;
} }

2
src/wallet.cpp

@ -780,7 +780,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate)) if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate))
ret++; ret++;
} }
pindex = pindex->pnext; pindex = pindex->GetNextInMainChain();
} }
} }
return ret; return ret;

Loading…
Cancel
Save