From b8585384da2c5de236431ebb71eec41aaac66b98 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 7 May 2013 18:33:52 +0200 Subject: [PATCH 1/3] Detect any sufficiently long fork and add a warning. Such a fork is defined as being at least 7 blocks long and having a tip which is within 72 blocks of our best block. --- src/main.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d35891440..2e79c874d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1383,6 +1383,56 @@ bool IsInitialBlockDownload() pindexBest->GetBlockTime() < GetTime() - 24 * 60 * 60); } +bool fLargeWorkForkFound = 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()) + { + fLargeWorkForkFound = true; + printf("CheckForkWarningConditions: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); + } else + fLargeWorkForkFound = 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) @@ -1398,8 +1448,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) { @@ -2110,11 +2159,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")); @@ -3073,8 +3125,7 @@ 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."); From f89faa258404d2e6fd4300990275cd2439e51255 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 7 May 2013 18:37:37 +0200 Subject: [PATCH 2/3] Call the -alertnotify script when we see a long or invalid fork. --- src/init.cpp | 2 +- src/main.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/init.cpp b/src/init.cpp index f6b2c91b4..14617809e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -225,7 +225,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 2e79c874d..b206b6803 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1395,6 +1395,16 @@ void CheckForkWarningConditions() if (pindexBestForkTip || nBestInvalidWork > nBestChainWork + (pindexBest->GetBlockWork() * 6).getuint256()) { + if (!fLargeWorkForkFound) + { + std::string strCmd = GetArg("-alertnotify", ""); + if (!strCmd.empty()) + { + std::string warning("'Warning: Large-work fork detected. You may need to upgrade, or other nodes may need to upgrade.'"); + boost::replace_all(strCmd, "%s", warning); + boost::thread t(runCommand, strCmd); // thread runs free + } + } fLargeWorkForkFound = true; printf("CheckForkWarningConditions: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); } else From f65e7092a200ee6e9453058c3dafbab62d9b4dcc Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Sat, 18 May 2013 03:09:28 +0200 Subject: [PATCH 3/3] Better warning/"alert" messages for large-work forks. --- src/main.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b206b6803..7dc51fc9d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1384,6 +1384,7 @@ bool IsInitialBlockDownload() } bool fLargeWorkForkFound = false; +bool fLargeWorkInvalidChainFound = false; CBlockIndex *pindexBestForkTip = NULL, *pindexBestForkBase = NULL; void CheckForkWarningConditions() @@ -1400,15 +1401,30 @@ void CheckForkWarningConditions() std::string strCmd = GetArg("-alertnotify", ""); if (!strCmd.empty()) { - std::string warning("'Warning: Large-work fork detected. You may need to upgrade, or other nodes may need to upgrade.'"); + 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 } } - fLargeWorkForkFound = true; - printf("CheckForkWarningConditions: Warning: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade.\n"); - } else + 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) @@ -3138,7 +3154,12 @@ string GetWarnings(string strFor) 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