diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-06-09 12:16:15 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2014-06-09 12:17:12 +0200 |
commit | 07b233a1b6e5f15ac7c8b3343088d1b2f3c8e1f1 (patch) | |
tree | 9ddc6e0b6866df01124b48355bb65deb1fa55411 /src/main.cpp | |
parent | 6ff35a0565a6903464d1dfaaf02360d5c5e70c0f (diff) | |
parent | 18e72167ddfeaea95253b62994c6d64b55b35005 (diff) |
Merge pull request #4148
18e7216 Push cs_mains down in ProcessBlock (Pieter Wuille)
202e019 Move all post-chaintip-change notifications to ActivateBestChain (Pieter Wuille)
4e0eed8 Allow ActivateBestChain to release its lock on cs_main (Pieter Wuille)
77339e5 Get rid of the static chainMostWork (optimization) (Pieter Wuille)
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 218 |
1 files changed, 120 insertions, 98 deletions
diff --git a/src/main.cpp b/src/main.cpp index 429473d8f8..16d1cba6ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,7 +40,6 @@ CTxMemPool mempool; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; -CChain chainMostWork; int64_t nTimeBestReceived = 0; int nScriptCheckThreads = 0; bool fImporting = false; @@ -398,6 +397,12 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const { return Genesis(); } +CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const { + while (pindex && !Contains(pindex)) + pindex = pindex->pprev; + return pindex; +} + CCoinsViewCache *pcoinsTip = NULL; CBlockTreeDB *pblocktree = NULL; @@ -1890,6 +1895,11 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C for (unsigned int i = 0; i < block.vtx.size(); i++) g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); + // Watch for changes to the previous coinbase transaction. + static uint256 hashPrevBestCoinBase; + g_signals.UpdatedTransaction(hashPrevBestCoinBase); + hashPrevBestCoinBase = block.GetTxHash(0); + return true; } @@ -2035,23 +2045,17 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) { return true; } -// Make chainMostWork correspond to the chain with the most work in it, that isn't +// Return the tip of the chain with the most work in it, that isn't // known to be invalid (it's however far from certain to be valid). -void static FindMostWorkChain() { - CBlockIndex *pindexNew = NULL; - - // In case the current best is invalid, do not consider it. - while (chainMostWork.Tip() && (chainMostWork.Tip()->nStatus & BLOCK_FAILED_MASK)) { - setBlockIndexValid.erase(chainMostWork.Tip()); - chainMostWork.SetTip(chainMostWork.Tip()->pprev); - } - +static CBlockIndex* FindMostWorkChain() { do { + CBlockIndex *pindexNew = NULL; + // Find the best candidate header. { std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin(); if (it == setBlockIndexValid.rend()) - return; + return NULL; pindexNew = *it; } @@ -2075,70 +2079,111 @@ void static FindMostWorkChain() { } pindexTest = pindexTest->pprev; } - if (fInvalidAncestor) - continue; - - break; + if (!fInvalidAncestor) + return pindexNew; } while(true); - - // Check whether it's actually an improvement. - if (chainMostWork.Tip() && !CBlockIndexWorkComparator()(chainMostWork.Tip(), pindexNew)) - return; - - // We have a new best. - chainMostWork.SetTip(pindexNew); } -// Try to activate to the most-work chain (thereby connecting it). -bool ActivateBestChain(CValidationState &state) { - LOCK(cs_main); +// Try to make some progress towards making pindexMostWork the active block. +static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork) { + AssertLockHeld(cs_main); + bool fInvalidFound = false; CBlockIndex *pindexOldTip = chainActive.Tip(); - bool fComplete = false; - while (!fComplete) { - FindMostWorkChain(); - fComplete = true; + CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); - // Check whether we have something to do. - if (chainMostWork.Tip() == NULL) break; + // Disconnect active blocks which are no longer in the best chain. + while (chainActive.Tip() && chainActive.Tip() != pindexFork) { + if (!DisconnectTip(state)) + return false; + } - // Disconnect active blocks which are no longer in the best chain. - while (chainActive.Tip() && !chainMostWork.Contains(chainActive.Tip())) { - if (!DisconnectTip(state)) - return false; - } + // Build list of new blocks to connect. + std::vector<CBlockIndex*> vpindexToConnect; + vpindexToConnect.reserve(pindexMostWork->nHeight - (pindexFork ? pindexFork->nHeight : -1)); + while (pindexMostWork && pindexMostWork != pindexFork) { + vpindexToConnect.push_back(pindexMostWork); + pindexMostWork = pindexMostWork->pprev; + } - // Connect new blocks. - while (!chainActive.Contains(chainMostWork.Tip())) { - CBlockIndex *pindexConnect = chainMostWork[chainActive.Height() + 1]; - if (!ConnectTip(state, pindexConnect)) { - if (state.IsInvalid()) { - // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(chainMostWork.Tip()); - fComplete = false; - state = CValidationState(); - break; - } else { - // A system error occurred (disk space, database error, ...). - return false; - } + // Connect new blocks. + BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) { + if (!ConnectTip(state, pindexConnect)) { + if (state.IsInvalid()) { + // The block violates a consensus rule. + if (!state.CorruptionPossible()) + InvalidChainFound(vpindexToConnect.back()); + state = CValidationState(); + fInvalidFound = true; + break; + } else { + // A system error occurred (disk space, database error, ...). + return false; + } + } else { + if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) { + // We're in a better position than we were. Return temporarily to release the lock. + break; } } } - if (chainActive.Tip() != pindexOldTip) { - std::string strCmd = GetArg("-blocknotify", ""); - if (!IsInitialBlockDownload() && !strCmd.empty()) + // Callbacks/notifications for a new best chain. + if (fInvalidFound) + CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); + else + CheckForkWarningConditions(); + + if (!pblocktree->Flush()) + return state.Abort(_("Failed to sync block index")); + + return true; +} + +bool ActivateBestChain(CValidationState &state) { + CBlockIndex *pindexNewTip = NULL; + CBlockIndex *pindexMostWork = NULL; + do { + boost::this_thread::interruption_point(); + + bool fInitialDownload; { - boost::replace_all(strCmd, "%s", chainActive.Tip()->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + LOCK(cs_main); + pindexMostWork = FindMostWorkChain(); + + // Whether we have anything to do at all. + if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) + return true; + + if (!ActivateBestChainStep(state, pindexMostWork)) + return false; + + pindexNewTip = chainActive.Tip(); + fInitialDownload = IsInitialBlockDownload(); } - } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). + + // Notifications/callbacks that can run without cs_main + if (!fInitialDownload) { + uint256 hashNewTip = pindexNewTip->GetBlockHash(); + // Relay inventory, but don't relay old inventory during initial block download. + int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) + pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip)); + + std::string strCmd = GetArg("-blocknotify", ""); + if (!strCmd.empty()) { + boost::replace_all(strCmd, "%s", hashNewTip.GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } + } + uiInterface.NotifyBlocksChanged(); + } while(pindexMostWork != chainActive.Tip()); return true; } - CBlockIndex* AddToBlockIndex(CBlockHeader& block) { // Check for duplicate @@ -2198,26 +2243,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) return state.Abort(_("Failed to write block index")); - // New best? - if (!ActivateBestChain(state)) - return false; - - LOCK(cs_main); - if (pindexNew == chainActive.Tip()) - { - // Clear fork warning if its no longer applicable - CheckForkWarningConditions(); - // Notify UI to display prev block's coinbase if it was ours - static uint256 hashPrevBestCoinBase; - g_signals.UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = block.GetTxHash(0); - } else - CheckForkWarningConditionsOnNewFork(pindexNew); - - if (!pblocktree->Flush()) - return state.Abort(_("Failed to sync block index")); - - uiInterface.NotifyBlocksChanged(); return true; } @@ -2494,7 +2519,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, } int nHeight = pindex->nHeight; - uint256 hash = pindex->GetBlockHash(); // Check that all transactions are finalized BOOST_FOREACH(const CTransaction& tx, block.vtx) @@ -2538,16 +2562,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return state.Abort(_("System error: ") + e.what()); } - // Relay inventory, but don't relay old inventory during initial block download - int nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(); - if (chainActive.Tip()->GetBlockHash() == hash) - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - if (chainActive.Height() > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) - pnode->PushInventory(CInv(MSG_BLOCK, hash)); - } - return true; } @@ -2577,10 +2591,11 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd) bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) { - AssertLockHeld(cs_main); - // Check for duplicate uint256 hash = pblock->GetHash(); + + { + LOCK(cs_main); if (mapBlockIndex.count(hash)) return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString()), 0, "duplicate"); if (mapOrphanBlocks.count(hash)) @@ -2649,7 +2664,11 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl mapOrphanBlocksByPrev.erase(hashPrev); } - LogPrintf("ProcessBlock: ACCEPTED\n"); + } + + if (!ActivateBestChain(state)) + return error("ProcessBlock() : ActivateBestChain failed"); + return true; } @@ -3085,6 +3104,8 @@ bool InitBlockIndex() { CBlockIndex *pindex = AddToBlockIndex(block); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); + if (!ActivateBestChain(state)) + return error("LoadBlockIndex() : genesis block cannot be activated"); } catch(std::runtime_error &e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); } @@ -3214,7 +3235,6 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // process block if (nBlockPos >= nStartByte) { - LOCK(cs_main); if (dbp) dbp->nPos = nBlockPos; CValidationState state; @@ -3903,10 +3923,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CInv inv(MSG_BLOCK, block.GetHash()); pfrom->AddInventoryKnown(inv); - LOCK(cs_main); - // Remember who we got this block from. - mapBlockSource[inv.hash] = pfrom->GetId(); - MarkBlockAsReceived(inv.hash, pfrom->GetId()); + { + LOCK(cs_main); + // Remember who we got this block from. + mapBlockSource[inv.hash] = pfrom->GetId(); + MarkBlockAsReceived(inv.hash, pfrom->GetId()); + } CValidationState state; ProcessBlock(state, pfrom, &block); |