diff --git a/src/init.cpp b/src/init.cpp index 20d334019d..09871c012c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -228,7 +228,7 @@ std::string HelpMessage() strUsage += " -rpcthreads= " + _("Set the number of threads to service RPC calls (default: 4)") + "\n"; strUsage += " -blocknotify= " + _("Execute command when the best block changes (%s in cmd is replaced by block hash)") + "\n"; strUsage += " -walletnotify= " + _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)") + "\n"; - strUsage += " -alertnotify= " + _("Execute command when a relevant alert is received (%s in cmd is replaced by message)") + "\n"; + strUsage += " -alertnotify= " + _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)") + "\n"; strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + "\n"; strUsage += " -keypool= " + _("Set key pool size to (default: 100)") + "\n"; strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n"; diff --git a/src/main.cpp b/src/main.cpp index 063536820d..e78e055147 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1376,6 +1376,82 @@ bool IsInitialBlockDownload() pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } +bool fLargeWorkForkFound = false; +bool fLargeWorkInvalidChainFound = false; +CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; + +void CheckForkWarningConditions() +{ + // If our best fork is no longer within 72 blocks (+/- 12 hours if no one mines it) + // of our head, drop it + if (pindexBestForkTip && nBestHeight - pindexBestForkTip->nHeight >= 72) + pindexBestForkTip = NULL; + + if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + { + if (!fLargeWorkForkFound) + { + std::string strCmd = GetArg("-alertnotify", ""); + if (!strCmd.empty()) + { + std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + + pindexBestForkBase->phashBlock->ToString() + std::string("'"); + boost::replace_all(strCmd, "%s", warning); + boost::thread t(runCommand, strCmd); // thread runs free + } + } + if (pindexBestForkTip) + { + printf("CheckForkWarningConditions: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", + pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString().c_str(), + pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString().c_str()); + fLargeWorkForkFound = true; + } + else + { + printf("CheckForkWarningConditions: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n"); + fLargeWorkInvalidChainFound = true; + } + } + else + { + fLargeWorkForkFound = false; + fLargeWorkInvalidChainFound = false; + } +} + +void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) +{ + // If we are on a fork that is sufficiently large, set a warning flag + CBlockIndex* pfork = pindexNewForkTip; + CBlockIndex* plonger = pindexBest; + while (pfork && pfork != plonger) + { + while (plonger && plonger->nHeight > pfork->nHeight) + plonger = plonger->pprev; + if (pfork == plonger) + break; + pfork = pfork->pprev; + } + + // We define a condition which we should warn the user about as a fork of at least 7 blocks + // who's tip is within 72 blocks (+/- 12 hours if no one mines it) of ours + // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network + // hash rate operating on the fork. + // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) + // We define it this way because it allows us to only store the highest fork tip (+ base) which meets + // the 7-block condition and from this always have the most-likely-to-cause-warning fork + if (pfork && (!pindexBestForkTip || (pindexBestForkTip && pindexNewForkTip->nHeight > pindexBestForkTip->nHeight)) && + pindexNewForkTip->nChainWork - pfork->nChainWork > (pfork->GetBlockWork() * 7).getuint256() && + nBestHeight - pindexNewForkTip->nHeight < 72) + { + pindexBestForkTip = pindexNewForkTip; + pindexBestForkBase = pfork; + } + + CheckForkWarningConditions(); +} + void static InvalidChainFound(CBlockIndex* pindexNew) { if (pindexNew->nChainWork > nBestInvalidWork) @@ -1391,8 +1467,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) printf("InvalidChainFound: current best=%s height=%d log2_work=%.8g date=%s\n", hashBestChain.ToString().c_str(), nBestHeight, log(nBestChainWork.getdouble())/log(2.0), DateTimeStrFormat("%Y-%m-%d %H:%M:%S", pindexBest->GetBlockTime()).c_str()); - if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) - printf("InvalidChainFound: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); + CheckForkWarningConditions(); } void static InvalidBlockFound(CBlockIndex *pindex) { @@ -2103,11 +2178,14 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos if (pindexNew == pindexBest) { + // Clear fork warning if its no longer applicable + CheckForkWarningConditions(); // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); hashPrevBestCoinBase = block.GetTxHash(0); - } + } else + CheckForkWarningConditionsOnNewFork(pindexNew); if (!pblocktree->Flush()) return state.Abort(_("Failed to sync block index")); @@ -3066,11 +3144,15 @@ string GetWarnings(string strFor) strStatusBar = strMiscWarning; } - // Longer invalid proof-of-work chain - if (pindexBest && nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) + if (fLargeWorkForkFound) { nPriority = 2000; - strStatusBar = strRPC = _("Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."); + strStatusBar = strRPC = _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."); + } + else if (fLargeWorkInvalidChainFound) + { + nPriority = 2000; + strStatusBar = strRPC = _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."); } // Alerts