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

54
src/main.cpp

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

24
src/main.h

@ -69,6 +69,7 @@ extern CScript COINBASE_FLAGS; @@ -69,6 +69,7 @@ extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
extern std::vector<CBlockIndex*> vBlockIndexByHeight;
extern std::set<CBlockIndex*, CBlockIndexWorkComparator> setBlockIndexValid;
extern uint256 hashGenesisBlock;
extern CBlockIndex* pindexGenesisBlock;
@ -1589,10 +1590,8 @@ enum BlockStatus { @@ -1589,10 +1590,8 @@ enum BlockStatus {
/** The block chain is a tree shaped structure starting with the
* 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
* main/longest chain. A blockindex may have multiple pprev pointing back
* 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.
* candidates to be the next block. A blockindex may have multiple pprev pointing
* to it, but at most one of them can be part of the currently active branch.
*/
class CBlockIndex
{
@ -1603,9 +1602,6 @@ public: @@ -1603,9 +1602,6 @@ public:
// pointer to the index of the predecessor of this block
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
int nHeight;
@ -1643,7 +1639,6 @@ public: @@ -1643,7 +1639,6 @@ public:
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nHeight = 0;
nFile = 0;
nDataPos = 0;
@ -1664,7 +1659,6 @@ public: @@ -1664,7 +1659,6 @@ public:
{
phashBlock = NULL;
pprev = NULL;
pnext = NULL;
nHeight = 0;
nFile = 0;
nDataPos = 0;
@ -1733,7 +1727,11 @@ public: @@ -1733,7 +1727,11 @@ public:
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
@ -1762,9 +1760,9 @@ public: @@ -1762,9 +1760,9 @@ public:
const CBlockIndex* pindex = this;
for (int i = 0; i < nMedianTimeSpan/2; i++)
{
if (!pindex->pnext)
if (!pindex->GetNextInMainChain())
return GetBlockTime();
pindex = pindex->pnext;
pindex = pindex->GetNextInMainChain();
}
return pindex->GetMedianTimePast();
}
@ -1779,7 +1777,7 @@ public: @@ -1779,7 +1777,7 @@ public:
std::string ToString() const
{
return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, pnext, nHeight,
pprev, GetNextInMainChain(), nHeight,
hashMerkleRoot.ToString().c_str(),
GetBlockHash().ToString().c_str());
}

5
src/rpcblockchain.cpp

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

2
src/wallet.cpp

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

Loading…
Cancel
Save