From ad870231cbb8d000dddbb20f49b3670bfbc9c4cd Mon Sep 17 00:00:00 2001 From: furszy Date: Sun, 29 Sep 2019 12:44:32 -0300 Subject: [PATCH 1/4] [Startup][Refactor][Backport] * OS memory allocation fail handler. * OS signal handler registration method created to remove code duplication. * AppInitBasicSetup() method created, organizing better the setup step of the wallet initialization. --- src/init.cpp | 64 ++++++++++++++++++++++++++++++++++++---------------- src/init.h | 6 +++++ 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index ff6503999689..00a57a013384 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -307,7 +307,7 @@ void Shutdown() */ void HandleSIGTERM(int) { - fRequestShutdown = true; + StartShutdown(); } void HandleSIGHUP(int) @@ -315,6 +315,17 @@ void HandleSIGHUP(int) fReopenDebugLog = true; } +#ifndef WIN32 +static void registerSignalHandler(int signal, void(*handler)(int)) +{ + struct sigaction sa; + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(signal, &sa, nullptr); +} +#endif + bool static InitError(const std::string& str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); @@ -748,10 +759,20 @@ bool AppInitServers() return true; } -/** Initialize pivx. - * @pre Parameters should be parsed and config file should be read. - */ -bool AppInit2() +[[noreturn]] static void new_handler_terminate() +{ + // Rather than throwing std::bad-alloc if allocation fails, terminate + // immediately to (try to) avoid chain corruption. + // Since LogPrintf may itself allocate memory, set the handler directly + // to terminate first. + std::set_new_handler(std::terminate); + LogPrintf("Error: Out of memory. Terminating.\n"); + + // The log was successful, terminate now. + std::terminate(); +}; + +bool AppInitBasicSetup() { // ********************************************************* Step 1: setup #ifdef _MSC_VER @@ -764,7 +785,7 @@ bool AppInit2() _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 -// Enable Data Execution Prevention (DEP) + // Enable Data Execution Prevention (DEP) // Minimum supported OS versions: WinXP SP3, WinVista >= SP1, Win Server 2008 // A failure is non-critical and needs no further attention! #ifndef PROCESS_DEP_ENABLE @@ -790,26 +811,31 @@ bool AppInit2() umask(077); } - - // Clean shutdown on SIGTERM - struct sigaction sa; - sa.sa_handler = HandleSIGTERM; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); + // Clean shutdown on SIGTERMx + registerSignalHandler(SIGTERM, HandleSIGTERM); + registerSignalHandler(SIGINT, HandleSIGTERM); // Reopen debug.log on SIGHUP - struct sigaction sa_hup; - sa_hup.sa_handler = HandleSIGHUP; - sigemptyset(&sa_hup.sa_mask); - sa_hup.sa_flags = 0; - sigaction(SIGHUP, &sa_hup, NULL); + registerSignalHandler(SIGHUP, HandleSIGHUP); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif + std::set_new_handler(new_handler_terminate); + + return true; +} + +/** Initialize pivx. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInit2() +{ + // ********************************************************* Step 1: setup + if (!AppInitBasicSetup()) + return false; + // ********************************************************* Step 2: parameter interactions // Set this early so that parameter interactions go to console fPrintToConsole = GetBoolArg("-printtoconsole", false); diff --git a/src/init.h b/src/init.h index 17aab3ef62b3..c2f0644d0664 100644 --- a/src/init.h +++ b/src/init.h @@ -29,6 +29,12 @@ void Shutdown(); void PrepareShutdown(); bool AppInit2(); +/** Initialize PIVX core: Basic context setup. + * @note This can be done before daemonization. Do not call Shutdown() if this function fails. + * @pre Parameters should be parsed and config file should be read. + */ +bool AppInitBasicSetup(); + /** The help message mode determines what help message to show */ enum HelpMessageMode { HMM_BITCOIND, From e0e9498c7a735da77d416224246407d7485c11aa Mon Sep 17 00:00:00 2001 From: furszy Date: Sun, 29 Sep 2019 14:09:23 -0300 Subject: [PATCH 2/4] [Logging][Startup] * Stop loading block indexes on wallet startup if shutdown was requested. * Wallet loading, wallet rescan and block index load time logged in a more understandable way. --- src/init.cpp | 18 ++++++++---------- src/main.cpp | 3 +++ src/util.cpp | 1 - src/util.h | 1 - 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 00a57a013384..36f27dfed409 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -958,7 +958,6 @@ bool AppInit2() else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; - fServer = GetBoolArg("-server", false); setvbuf(stdout, NULL, _IOLBF, 0); /// ***TODO*** do we still need this after -printtoconsole is gone? // Staking needs a CWallet instance, so make sure wallet is enabled @@ -1114,14 +1113,12 @@ bool AppInit2() * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ - if (fServer) { + if (GetBoolArg("-server", false)) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers()) return InitError(_("Unable to start HTTP server. See debug log for details.")); } - int64_t nStart; - // ********************************************************* Step 5: Backup wallet and verify wallet database integrity #ifdef ENABLE_WALLET if (!fDisableWallet) { @@ -1443,8 +1440,9 @@ bool AppInit2() uiInterface.InitMessage(_("Loading block index...")); - nStart = GetTimeMillis(); do { + const int64_t load_block_index_start_time = GetTimeMillis(); + try { UnloadBlockIndex(); delete pcoinsTip; @@ -1615,6 +1613,7 @@ bool AppInit2() fVerifyingBlocks = false; fLoaded = true; + LogPrintf(" block index %15dms\n", GetTimeMillis() - load_block_index_start_time); } while (false); if (!fLoaded && !ShutdownRequested()) { @@ -1643,7 +1642,6 @@ bool AppInit2() LogPrintf("Shutdown requested. Exiting.\n"); return false; } - LogPrintf(" block index %15dms\n", GetTimeMillis() - nStart); boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); @@ -1679,7 +1677,7 @@ bool AppInit2() uiInterface.InitMessage(_("Loading wallet...")); fVerifyingBlocks = true; - nStart = GetTimeMillis(); + const int64_t nWalletStartTime = GetTimeMillis(); bool fFirstRun = true; pwalletMain = new CWallet(strWalletFile); DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); @@ -1727,7 +1725,7 @@ bool AppInit2() } LogPrintf("%s", strErrors.str()); - LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); + LogPrintf("Wallet completed loading in %15dms\n", GetTimeMillis() - nWalletStartTime); zwalletMain = new CzPIVWallet(pwalletMain->strWalletFile); pwalletMain->setZWallet(zwalletMain); @@ -1747,9 +1745,9 @@ bool AppInit2() if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); - nStart = GetTimeMillis(); + const int64_t nWalletRescanTime = GetTimeMillis(); pwalletMain->ScanForWalletTransactions(pindexRescan, true); - LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); + LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; diff --git a/src/main.cpp b/src/main.cpp index 8cc685240bf5..f7e982073157 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5293,6 +5293,9 @@ bool static LoadBlockIndexDB(std::string& strError) } std::sort(vSortedByHeight.begin(), vSortedByHeight.end()); for (const PAIRTYPE(int, CBlockIndex*) & item : vSortedByHeight) { + // Stop if shutdown was requested + if (ShutdownRequested()) return false; + CBlockIndex* pindex = item.second; pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + GetBlockProof(*pindex); if (pindex->nStatus & BLOCK_HAVE_DATA) { diff --git a/src/util.cpp b/src/util.cpp index 0a3791e686b6..4ee4cea936fd 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -114,7 +114,6 @@ bool fDebug = false; bool fPrintToConsole = false; bool fPrintToDebugLog = true; bool fDaemon = false; -bool fServer = false; std::string strMiscWarning; bool fLogTimestamps = false; bool fLogIPs = false; diff --git a/src/util.h b/src/util.h index d0280a3d5a77..e9b08fc8d6a5 100644 --- a/src/util.h +++ b/src/util.h @@ -53,7 +53,6 @@ extern std::map > mapMultiArgs; extern bool fDebug; extern bool fPrintToConsole; extern bool fPrintToDebugLog; -extern bool fServer; extern std::string strMiscWarning; extern bool fLogTimestamps; extern bool fLogIPs; From 669794a5ba298809c1298cfe2c64bce4e0cf9d93 Mon Sep 17 00:00:00 2001 From: furszy Date: Wed, 2 Oct 2019 13:22:38 -0300 Subject: [PATCH 3/4] [Startup][Wallet] Don't continue rescanning the wallet if shutdown was requested. --- src/init.cpp | 5 ++++- src/wallet/wallet.cpp | 9 ++++++++- src/wallet/wallet.h | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 36f27dfed409..04f998f0bce5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1746,7 +1746,10 @@ bool AppInit2() uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); const int64_t nWalletRescanTime = GetTimeMillis(); - pwalletMain->ScanForWalletTransactions(pindexRescan, true); + if (pwalletMain->ScanForWalletTransactions(pindexRescan, true, true) == -1) { + LogPrintf("Shutdown requested over the txs scan. Exiting.\n"); + return false; + } LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); pwalletMain->SetBestChain(chainActive.GetLocator()); nWalletDBUpdated++; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 49d26c3b19c4..dea8589e39fc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1264,8 +1264,9 @@ bool CWalletTx::WriteToDisk() * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. + * @returns -1 if process was cancelled or the number of tx added to the wallet. */ -int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) +int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate, bool fromStartup) { int ret = 0; int64_t nNow = GetTime(); @@ -1290,6 +1291,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); + if (fromStartup) { + if (ShutdownRequested()) { + return -1; + } + } + CBlock block; ReadBlockFromDisk(block, pindex); for (CTransaction& tx : block.vtx) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f3562dfe4e72..8b0c90a4f627 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -506,7 +506,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void SyncTransaction(const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256& hash); - int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); + int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false, bool fromStartup = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(); CAmount GetBalance() const; From eafe9cdbb5dbad16d6d329c81dba2cd035cd96d9 Mon Sep 17 00:00:00 2001 From: furszy Date: Thu, 3 Oct 2019 10:50:04 -0300 Subject: [PATCH 4/4] [Trivial] Use error() instead of LogPrintf + return false. --- src/init.cpp | 3 +-- src/wallet/wallet.cpp | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 04f998f0bce5..1183ac727e8a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1747,8 +1747,7 @@ bool AppInit2() LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); const int64_t nWalletRescanTime = GetTimeMillis(); if (pwalletMain->ScanForWalletTransactions(pindexRescan, true, true) == -1) { - LogPrintf("Shutdown requested over the txs scan. Exiting.\n"); - return false; + return error("Shutdown requested over the txs scan. Exiting."); } LogPrintf("Rescan completed in %15dms\n", GetTimeMillis() - nWalletRescanTime); pwalletMain->SetBestChain(chainActive.GetLocator()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index dea8589e39fc..d969709264be 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1291,10 +1291,8 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate, b if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); - if (fromStartup) { - if (ShutdownRequested()) { - return -1; - } + if (fromStartup && ShutdownRequested()) { + return -1; } CBlock block;