Browse Source

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)
try
Wladimir J. van der Laan 11 years ago
parent
commit
07b233a1b6
No known key found for this signature in database GPG Key ID: 74810B012346C9A6
  1. 218
      src/main.cpp
  2. 6
      src/main.h
  3. 24
      src/miner.cpp
  4. 2
      src/rpcserver.cpp

218
src/main.cpp

@ -40,7 +40,6 @@ CTxMemPool mempool;
map<uint256, CBlockIndex*> mapBlockIndex; map<uint256, CBlockIndex*> mapBlockIndex;
CChain chainActive; CChain chainActive;
CChain chainMostWork;
int64_t nTimeBestReceived = 0; int64_t nTimeBestReceived = 0;
int nScriptCheckThreads = 0; int nScriptCheckThreads = 0;
bool fImporting = false; bool fImporting = false;
@ -398,6 +397,12 @@ CBlockIndex *CChain::FindFork(const CBlockLocator &locator) const {
return Genesis(); return Genesis();
} }
CBlockIndex *CChain::FindFork(CBlockIndex *pindex) const {
while (pindex && !Contains(pindex))
pindex = pindex->pprev;
return pindex;
}
CCoinsViewCache *pcoinsTip = NULL; CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = 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++) for (unsigned int i = 0; i < block.vtx.size(); i++)
g_signals.SyncTransaction(block.GetTxHash(i), block.vtx[i], &block); 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; return true;
} }
@ -2035,23 +2045,17 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew) {
return true; 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). // known to be invalid (it's however far from certain to be valid).
void static FindMostWorkChain() { static CBlockIndex* 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);
}
do { do {
CBlockIndex *pindexNew = NULL;
// Find the best candidate header. // Find the best candidate header.
{ {
std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin(); std::set<CBlockIndex*, CBlockIndexWorkComparator>::reverse_iterator it = setBlockIndexValid.rbegin();
if (it == setBlockIndexValid.rend()) if (it == setBlockIndexValid.rend())
return; return NULL;
pindexNew = *it; pindexNew = *it;
} }
@ -2075,70 +2079,111 @@ void static FindMostWorkChain() {
} }
pindexTest = pindexTest->pprev; pindexTest = pindexTest->pprev;
} }
if (fInvalidAncestor) if (!fInvalidAncestor)
continue; return pindexNew;
break;
} while(true); } 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). // Try to make some progress towards making pindexMostWork the active block.
bool ActivateBestChain(CValidationState &state) { static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork) {
LOCK(cs_main); AssertLockHeld(cs_main);
bool fInvalidFound = false;
CBlockIndex *pindexOldTip = chainActive.Tip(); CBlockIndex *pindexOldTip = chainActive.Tip();
bool fComplete = false; CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork);
while (!fComplete) {
FindMostWorkChain();
fComplete = true;
// Check whether we have something to do. // Disconnect active blocks which are no longer in the best chain.
if (chainMostWork.Tip() == NULL) break; while (chainActive.Tip() && chainActive.Tip() != pindexFork) {
if (!DisconnectTip(state))
return false;
}
// Disconnect active blocks which are no longer in the best chain. // Build list of new blocks to connect.
while (chainActive.Tip() && !chainMostWork.Contains(chainActive.Tip())) { std::vector<CBlockIndex*> vpindexToConnect;
if (!DisconnectTip(state)) vpindexToConnect.reserve(pindexMostWork->nHeight - (pindexFork ? pindexFork->nHeight : -1));
return false; while (pindexMostWork && pindexMostWork != pindexFork) {
} vpindexToConnect.push_back(pindexMostWork);
pindexMostWork = pindexMostWork->pprev;
}
// Connect new blocks. // Connect new blocks.
while (!chainActive.Contains(chainMostWork.Tip())) { BOOST_REVERSE_FOREACH(CBlockIndex *pindexConnect, vpindexToConnect) {
CBlockIndex *pindexConnect = chainMostWork[chainActive.Height() + 1]; if (!ConnectTip(state, pindexConnect)) {
if (!ConnectTip(state, pindexConnect)) { if (state.IsInvalid()) {
if (state.IsInvalid()) { // The block violates a consensus rule.
// The block violates a consensus rule. if (!state.CorruptionPossible())
if (!state.CorruptionPossible()) InvalidChainFound(vpindexToConnect.back());
InvalidChainFound(chainMostWork.Tip()); state = CValidationState();
fComplete = false; fInvalidFound = true;
state = CValidationState(); break;
break; } else {
} else { // A system error occurred (disk space, database error, ...).
// A system error occurred (disk space, database error, ...). return false;
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) { // Callbacks/notifications for a new best chain.
std::string strCmd = GetArg("-blocknotify", ""); if (fInvalidFound)
if (!IsInitialBlockDownload() && !strCmd.empty()) 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()); LOCK(cs_main);
boost::thread t(runCommand, strCmd); // thread runs free 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; return true;
} }
CBlockIndex* AddToBlockIndex(CBlockHeader& block) CBlockIndex* AddToBlockIndex(CBlockHeader& block)
{ {
// Check for duplicate // Check for duplicate
@ -2198,26 +2243,6 @@ bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBl
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew))) if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
return state.Abort(_("Failed to write block index")); 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; return true;
} }
@ -2494,7 +2519,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
} }
int nHeight = pindex->nHeight; int nHeight = pindex->nHeight;
uint256 hash = pindex->GetBlockHash();
// Check that all transactions are finalized // Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx) 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()); 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; return true;
} }
@ -2577,10 +2591,11 @@ void PushGetBlocks(CNode* pnode, CBlockIndex* pindexBegin, uint256 hashEnd)
bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp) bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBlockPos *dbp)
{ {
AssertLockHeld(cs_main);
// Check for duplicate // Check for duplicate
uint256 hash = pblock->GetHash(); uint256 hash = pblock->GetHash();
{
LOCK(cs_main);
if (mapBlockIndex.count(hash)) if (mapBlockIndex.count(hash))
return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString()), 0, "duplicate"); return state.Invalid(error("ProcessBlock() : already have block %d %s", mapBlockIndex[hash]->nHeight, hash.ToString()), 0, "duplicate");
if (mapOrphanBlocks.count(hash)) if (mapOrphanBlocks.count(hash))
@ -2649,7 +2664,11 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
mapOrphanBlocksByPrev.erase(hashPrev); mapOrphanBlocksByPrev.erase(hashPrev);
} }
LogPrintf("ProcessBlock: ACCEPTED\n"); }
if (!ActivateBestChain(state))
return error("ProcessBlock() : ActivateBestChain failed");
return true; return true;
} }
@ -3085,6 +3104,8 @@ bool InitBlockIndex() {
CBlockIndex *pindex = AddToBlockIndex(block); CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex() : genesis block not accepted"); return error("LoadBlockIndex() : genesis block not accepted");
if (!ActivateBestChain(state))
return error("LoadBlockIndex() : genesis block cannot be activated");
} catch(std::runtime_error &e) { } catch(std::runtime_error &e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());
} }
@ -3214,7 +3235,6 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
// process block // process block
if (nBlockPos >= nStartByte) { if (nBlockPos >= nStartByte) {
LOCK(cs_main);
if (dbp) if (dbp)
dbp->nPos = nBlockPos; dbp->nPos = nBlockPos;
CValidationState state; CValidationState state;
@ -3903,10 +3923,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
CInv inv(MSG_BLOCK, block.GetHash()); CInv inv(MSG_BLOCK, block.GetHash());
pfrom->AddInventoryKnown(inv); pfrom->AddInventoryKnown(inv);
LOCK(cs_main); {
// Remember who we got this block from. LOCK(cs_main);
mapBlockSource[inv.hash] = pfrom->GetId(); // Remember who we got this block from.
MarkBlockAsReceived(inv.hash, pfrom->GetId()); mapBlockSource[inv.hash] = pfrom->GetId();
MarkBlockAsReceived(inv.hash, pfrom->GetId());
}
CValidationState state; CValidationState state;
ProcessBlock(state, pfrom, &block); ProcessBlock(state, pfrom, &block);

6
src/main.h

@ -1079,14 +1079,14 @@ public:
/** Find the last common block between this chain and a locator. */ /** Find the last common block between this chain and a locator. */
CBlockIndex *FindFork(const CBlockLocator &locator) const; CBlockIndex *FindFork(const CBlockLocator &locator) const;
/** Find the last common block between this chain and a block index entry. */
CBlockIndex *FindFork(CBlockIndex *pindex) const;
}; };
/** The currently-connected chain of blocks. */ /** The currently-connected chain of blocks. */
extern CChain chainActive; extern CChain chainActive;
/** The currently best known chain of headers (some of which may be invalid). */
extern CChain chainMostWork;
/** Global variable that points to the active CCoinsView (protected by cs_main) */ /** Global variable that points to the active CCoinsView (protected by cs_main) */
extern CCoinsViewCache *pcoinsTip; extern CCoinsViewCache *pcoinsTip;

24
src/miner.cpp

@ -484,22 +484,22 @@ bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
LOCK(cs_main); LOCK(cs_main);
if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash()) if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
return error("BitcoinMiner : generated block is stale"); return error("BitcoinMiner : generated block is stale");
}
// Remove key from key pool // Remove key from key pool
reservekey.KeepKey(); reservekey.KeepKey();
// Track how many getdata requests this block gets
{
LOCK(wallet.cs_wallet);
wallet.mapRequestCount[pblock->GetHash()] = 0;
}
// Process this block the same as if we had received it from another node // Track how many getdata requests this block gets
CValidationState state; {
if (!ProcessBlock(state, NULL, pblock)) LOCK(wallet.cs_wallet);
return error("BitcoinMiner : ProcessBlock, block not accepted"); wallet.mapRequestCount[pblock->GetHash()] = 0;
} }
// Process this block the same as if we had received it from another node
CValidationState state;
if (!ProcessBlock(state, NULL, pblock))
return error("BitcoinMiner : ProcessBlock, block not accepted");
return true; return true;
} }

2
src/rpcserver.cpp

@ -254,7 +254,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getblocktemplate", &getblocktemplate, true, false, false }, { "getblocktemplate", &getblocktemplate, true, false, false },
{ "getmininginfo", &getmininginfo, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false },
{ "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getnetworkhashps", &getnetworkhashps, true, false, false },
{ "submitblock", &submitblock, false, false, false }, { "submitblock", &submitblock, false, true, false },
/* Raw transactions */ /* Raw transactions */
{ "createrawtransaction", &createrawtransaction, false, false, false }, { "createrawtransaction", &createrawtransaction, false, false, false },

Loading…
Cancel
Save