Merge pull request #2644 from sipa/constfindblock

Make FindBlockByHeight constant-time
This commit is contained in:
Jeff Garzik 2013-05-30 08:06:44 -07:00
commit d397715661
5 changed files with 36 additions and 51 deletions

View File

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

View File

@ -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();
@ -1846,15 +1837,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) {
@ -2587,12 +2573,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,
@ -2661,7 +2647,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());
@ -2837,7 +2823,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;
@ -3418,10 +3404,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)
{ {
@ -3461,14 +3447,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)

View File

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

View File

@ -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;
} }

View File

@ -779,7 +779,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;