/* * This file is part of the Flowee project * Copyright (c) 2009-2010 Satoshi Nakamoto * Copyright (c) 2009-2015 The Bitcoin Core developers * Copyright (C) 2019-2024 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #if defined(HAVE_CONFIG_H) #include "config/flowee-config.h" #endif #include "init.h" #include #include "Application.h" #include "addrman.h" #include "amount.h" #include "chain.h" #include "chainparams.h" #include "checkpoints.h" #include "compat/sanity.h" #include "DoubleSpendProofStorage.h" #include "httpserver.h" #include "httprpc.h" #include #include "main.h" #include "miner.h" #include "net.h" #include "policy/policy.h" #include "rpcserver.h" #include "script/standard.h" #include "script/sigcache.h" #include "scheduler.h" #include "BlocksDB.h" #include "txmempool.h" #include "torcontrol.h" #include "UiInterface.h" #include #include #include "serverutil.h" #include #include #include "txorphancache.h" #include "validationinterface.h" #include #include #include #ifndef WIN32 #include #include #endif #include #include #include #include #include #include #include // for to_lower() #include #include #include #include #include #include #include #if ENABLE_ZMQ #include "zmq/zmqnotificationinterface.h" #endif #if ENABLE_ZMQ static CZMQNotificationInterface* pzmqNotificationInterface = NULL; #endif #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for // accessing block files don't count towards the fd_set size limit // anyway. #define MIN_CORE_FILEDESCRIPTORS 0 #else #define MIN_CORE_FILEDESCRIPTORS 1000 #endif /** Used to pass flags to the Bind() function */ enum BindFlags { BF_NONE = 0, BF_EXPLICIT = (1U << 0), BF_REPORT_ERROR = (1U << 1), BF_WHITELIST = (1U << 2), }; CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h namespace { /** * this function tries to raise the file descriptor limit to the requested number. * It returns the actual file descriptor limit (which may be more or less than nMinFD) */ int RaiseFileDescriptorLimit(int nMinFD) { #if defined(WIN32) return 2048; #else struct rlimit limitFD; if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) { if (limitFD.rlim_cur < (rlim_t)nMinFD) { limitFD.rlim_cur = nMinFD; if (limitFD.rlim_cur > limitFD.rlim_max) limitFD.rlim_cur = limitFD.rlim_max; setrlimit(RLIMIT_NOFILE, &limitFD); getrlimit(RLIMIT_NOFILE, &limitFD); } return limitFD.rlim_cur; } return nMinFD; // getrlimit failed, assume it's fine #endif } #ifndef WIN32 boost::filesystem::path GetPidFile() { boost::filesystem::path pathPidFile(GetArg("-pid", Settings::hubPidFilename())); if (!pathPidFile.is_absolute()) pathPidFile = GetDataDir() / pathPidFile; return pathPidFile; } void CreatePidFile(const boost::filesystem::path &path, pid_t pid) { FILE* file = fopen(path.string().c_str(), "w"); if (file) { fprintf(file, "%d\n", pid); fclose(file); } } #endif void ShrinkDebugFile() { // Scroll hub.log if it's getting too big boost::filesystem::path pathLog = GetDataDir() / "hub.log"; FILE* file = fopen(pathLog.string().c_str(), "r"); if (file && boost::filesystem::file_size(pathLog) > 10 * 1000000) { // Restart the file with some of the end std::vector vch(200000,0); fseek(file, -((long)vch.size()), SEEK_END); int nBytes = fread(begin_ptr(vch), 1, vch.size(), file); fclose(file); file = fopen(pathLog.string().c_str(), "w"); if (file) { fwrite(begin_ptr(vch), 1, nBytes, file); fclose(file); } } else if (file != NULL) fclose(file); } } ////////////////////////////////////////////////////////////////////////////// // // Shutdown // // // Thread management and startup/shutdown: // // The network-processing threads are all part of a thread group // created by AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM // signal handler sets fRequestShutdown, which triggers // the DetectShutdownThread(), which interrupts the main thread group. // DetectShutdownThread() then exits, which causes AppInit() to // continue (it .joins the shutdown thread). // Shutdown() is then // called to clean up database connections, and stop other // threads that should only be stopped after the main network-processing // threads have exited. // // Note that if running -daemon the parent process returns from AppInit2 // before adding any threads to the threadGroup, so .join_all() returns // immediately and the parent exits from main(). // // Shutdown for Qt is very similar, only it uses a QTimer to detect // fRequestShutdown getting set, and then does the normal Qt // shutdown thing. // volatile bool fRequestShutdown = false; void StartShutdown() { fRequestShutdown = true; } bool ShutdownRequested() { return fRequestShutdown; } static boost::scoped_ptr globalVerifyHandle; UnspentOutputDatabase *g_utxo = nullptr; void Interrupt(boost::thread_group& threadGroup) { InterruptHTTPServer(); InterruptRPC(); InterruptTorControl(); threadGroup.interrupt_all(); } void Shutdown() { logCritical(Log::Bitcoin) << "Shutdown in progress..."; static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); if (!lockShutdown) return; /// Note: Shutdown() must be able to handle cases in which AppInit2() failed part of the way, /// for example if the data directory was found to be locked. /// Be sure that anything that writes files or flushes caches only does this if the respective /// module was initialized. RenameThread("hub-shutoff"); mempool.AddTransactionsUpdated(1); StopHTTPRPC(); StopREST(); StopRPC(); StopHTTPServer(); Mining::Stop(); StopNode(); StopTorControl(); UnregisterNodeSignals(GetNodeSignals()); Application::quit(0); Application::exec(); // waits for threads to finish. { LOCK(cs_main); FlushStateToDisk(); delete g_utxo; g_utxo = nullptr; Blocks::DB::shutdown(); } #if ENABLE_ZMQ if (pzmqNotificationInterface) { ValidationNotifier().removeListener(pzmqNotificationInterface); delete pzmqNotificationInterface; pzmqNotificationInterface = NULL; } #endif #ifndef WIN32 try { boost::filesystem::remove(GetPidFile()); } catch (const boost::filesystem::filesystem_error& e) { logCritical(Log::Bitcoin) << "Shutdown: Unable to remove pidfile:" << e; } #endif ValidationNotifier().removeAll(); globalVerifyHandle.reset(); ECC_Stop(); logCritical(Log::Bitcoin) << "Shutdown: done"; } /** * Signal handlers are very limited in what they are allowed to do, so: */ void HandleSIGTERM(int) { fRequestShutdown = true; } void HandleSIGHUP(int) { Log::Manager::instance()->reopenLogFiles(); Log::Manager::instance()->parseConfig(GetConfigFile("logs.conf"), GetDataDir(true) / "hub.log"); } bool static InitError(const std::string &str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; } bool static InitWarning(const std::string &str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); return true; } bool static Bind(const CService &addr, unsigned int flags) { if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { if (flags & BF_REPORT_ERROR) return InitError(strError); return false; } return true; } void OnRPCStopped() { cvBlockChange.notify_all(); logInfo(Log::RPC) << "RPC stopped."; } void OnRPCPreCommand(const CRPCCommand& cmd) { // Observe safe mode std::string strWarning = GetWarnings("rpc"); if (strWarning != "" && !GetBoolArg("-disablesafemode", Settings::DefaultDisableSafemode) && !cmd.okSafeMode) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); } static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex) { if (initialSync || !pBlockIndex) return; std::string strCmd = GetArg("-blocknotify", ""); boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } /** Sanity checks * Ensure that Bitcoin is running in a usable environment with all * necessary library support. */ bool InitSanityCheck(void) { if(!ECC_InitSanityCheck()) { InitError("Elliptic curve cryptography sanity check failure. Aborting."); return false; } if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; return true; } bool AppInitServers() { RPCServer::OnStopped(&OnRPCStopped); RPCServer::OnPreCommand(&OnRPCPreCommand); if (!InitHTTPServer()) return false; StartRPC(); if (!StartHTTPRPC()) return false; if (GetBoolArg("-rest", Settings::DefaultRestEnable)) StartREST(); StartHTTPServer(); return true; } // Parameter interaction based on rules void InitParameterInteraction() { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified if (mapArgs.count("-bind")) { if (SoftSetBoolArg("-listen", true)) logCritical(Log::Net) << "parameter interaction: -bind set -> setting -listen=1"; } if (mapArgs.count("-whitebind")) { if (SoftSetBoolArg("-listen", true)) logCritical(Log::Net) << "parameter interaction: -whitebind set -> setting -listen=1"; } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { // when only connecting to trusted nodes, do not seed via DNS, or listen by default if (SoftSetBoolArg("-dnsseed", false)) logCritical(Log::Net) << "parameter interaction: -connect set -> setting -dnsseed=0"; if (SoftSetBoolArg("-listen", false)) logCritical(Log::Net) << "parameter interaction: -connect set -> setting -listen=0"; } if (mapArgs.count("-proxy")) { // to protect privacy, do not listen by default if a default proxy server is specified if (SoftSetBoolArg("-listen", false)) logCritical(Log::Proxy) << "parameter interaction: -proxy set -> setting -listen=0"; // to protect privacy, do not use UPNP when a proxy is set. The user may still specify -listen=1 // to listen locally, so don't rely on this happening through -listen below. if (SoftSetBoolArg("-upnp", false)) logCritical(Log::Proxy) << "parameter interaction: -proxy set -> setting -upnp=0"; // to protect privacy, do not discover addresses by default if (SoftSetBoolArg("-discover", false)) logCritical(Log::Proxy) << "parameter interaction: -proxy set -> setting -discover=0"; } if (!GetBoolArg("-listen", DEFAULT_LISTEN)) { // do not map ports or try to retrieve public IP when not listening (pointless) if (SoftSetBoolArg("-upnp", false)) logCritical(Log::Net) << "parameter interaction: -listen=0 -> setting -upnp=0"; if (SoftSetBoolArg("-discover", false)) logCritical(Log::Net) << "parameter interaction: -listen=0 -> setting -discover=0"; if (SoftSetBoolArg("-listenonion", false)) logCritical(Log::Net) << "parameter interaction: -listen=0 -> setting -listenonion=0"; } if (mapArgs.count("-externalip")) { // if an explicit public IP is specified, do not try to find others if (SoftSetBoolArg("-discover", false)) logCritical(Log::Net) << "parameter interaction: -externalip set -> setting -discover=false"; } // disable walletbroadcast and whitelistrelay in blocksonly mode if (GetBoolArg("-blocksonly", Settings::DefaultBlocksOnly)) { if (SoftSetBoolArg("-whitelistrelay", false)) logCritical(Log::Net) << "parameter interaction: -blocksonly=true -> setting -whitelistrelay=false"; } // Forcing relay from whitelisted hosts implies we will accept relays from them in the first place. if (GetBoolArg("-whitelistforcerelay", Settings::DefaultWhitelistForceRelay)) { if (SoftSetBoolArg("-whitelistrelay", true)) logCritical(Log::Net) << "parameter interaction: -whitelistforcerelay=true -> setting -whitelistrelay=true"; } auto miningSize = GetArg("-blockmaxsize", -1); if (miningSize > 0x7FFFFFFF) { // overflow logCritical(Log::Mining) << "parameter -blockmaxsize is too large. Max is 31bit int"; throw std::runtime_error("invalid parameter passed to -blockmaxsize"); } int32_t acceptSize = Policy::blockSizeAcceptLimit(); if (GetBoolArg("-scalenet", false) && acceptSize < 268435456) { logCritical(Log::Net) << "parameter interaction: -scalenet -> setting -blockmaxsize"; miningSize = acceptSize = 268435456; SoftSetArg("-blocksizeacceptlimit", "270"); SoftSetArg("-blockmaxsize", "268435456"); } if ((int) miningSize > acceptSize) { if (SoftSetArg("-blocksizeacceptlimit", boost::lexical_cast((miningSize + 100000) / 1E6))) logCritical(Log::Net) << "parameter interaction: -blockmaxsize N -> setting -blockacceptlimit=N"; else throw std::runtime_error("Block Accept setting smaller than block mining size. Please adjust and restart"); } assert(Policy::blockSizeAcceptLimit() >= miningSize); const int64_t mempoolMaxSize = GetArg("-maxmempool", Settings::DefaultMaxMempoolSize) * 4500000; if (mempoolMaxSize < miningSize * 4) { if (SoftSetArg("-maxmempool", boost::lexical_cast(miningSize * 4))) logCritical(Log::Net) << "parameter interaction: -blockmaxsize N -> setting -maxmempool=4N"; } } void InitLogging() { fLogIPs = GetBoolArg("-logips", DEFAULT_LOGIPS); auto *manager = Log::Manager::instance(); #if 1 manager->parseConfig(GetConfigFile("logs.conf"), GetDataDir(true) / "hub.log"); #else // this is high speed logging. Possibly filling up your memory, only useful for timing specific work. manager->clearChannels(); manager->addFastLogChannel(GetDataDir(true) / "hub.log"); #endif logCritical(Log::Bitcoin) << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; logCritical(Log::Bitcoin) << "Flowee the Hub version " << CLIENT_BUILD.c_str() << "Built:" << CLIENT_DATE.c_str(); } /** Initialize bitcoin. * @pre Parameters should be parsed and config file should be read. */ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) { // ********************************************************* Step 1: setup #ifdef _MSC_VER // Turn off Microsoft heap dump noise _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, CreateFileA("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); #endif #if _MSC_VER >= 1400 // Disable confusing "helpful" text message on abort, Ctrl-C _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif #ifdef WIN32 // 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 // We define this here, because GCCs winbase.h limits this to _WIN32_WINNT >= 0x0601 (Windows 7), // which is not correct. Can be removed, when GCCs winbase.h is fixed! #define PROCESS_DEP_ENABLE 0x00000001 #endif typedef BOOL (WINAPI *PSETPROCDEPPOL)(DWORD); PSETPROCDEPPOL setProcDEPPol = (PSETPROCDEPPOL)GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetProcessDEPPolicy"); if (setProcDEPPol != NULL) setProcDEPPol(PROCESS_DEP_ENABLE); #endif #ifdef USE_UNSAFE_RANDOM /* Intializes random number generator in case the operator wanted non-safe random */ srand(unsigned(boost::posix_time::microsec_clock::local_time().time_of_day().total_microseconds())); #endif if (!SetupNetworking()) return InitError("Initializing networking failed"); #ifndef WIN32 // 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); // Reopen hub.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); // Ignore SIGPIPE, otherwise it will bring the daemon down if the client closes unexpectedly signal(SIGPIPE, SIG_IGN); #endif // ********************************************************* Step 2: parameter interactions const CChainParams& chainparams = Params(); // also see: InitParameterInteraction() // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); int nUserMaxConnections = GetArg("-maxconnections", Settings::DefaultMaxPeerConnections); nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS + nBind); nMaxConnections = std::max(std::min(nMaxConnections, (int)(nFD - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); logFatal() << "num file descriptors:" << nFD; if (nFD < MIN_CORE_FILEDESCRIPTORS) return InitError(_("Not enough file descriptors available.")); nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS, nMaxConnections); if (nMaxConnections < nUserMaxConnections) InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections)); // ********************************************************* Step 3: parameter-to-internal-flags fCheckpointsEnabled = GetBoolArg("-checkpoints", Settings::DefaultCheckpointsEnabled); // mempool limits int64_t nMempoolSizeMax = GetArg("-maxmempool", Settings::DefaultMaxMempoolSize) * 1000000; int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", Settings::DefaultDescendantSizeLimit) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); fServer = GetBoolArg("-server", false); nConnectTimeout = GetArg("-timeout", Settings::DefaultConnectTimeout); if (nConnectTimeout <= 0) nConnectTimeout = Settings::DefaultConnectTimeout; // Fee-per-kilobyte amount considered the same as "free" // If you are mining, be careful setting this: // if you set it to zero then // a transaction spammer can cheaply fill blocks using // 1-satoshi-fee transactions. It should be set above the real // cost to you of processing a transaction. if (mapArgs.count("-minrelaytxfee")) { int64_t n = 0; if (ParseMoney(mapArgs["-minrelaytxfee"], n) && n > 0) { logCritical(Log::Bitcoin) << "Setting min relay Transaction fee to" << n << "satoshi"; ::minRelayTxFee = CFeeRate(n); } else { return InitError(strprintf(_("Invalid amount for -minrelaytxfee=: '%s'"), mapArgs["-minrelaytxfee"])); } } fRequireStandard = !GetBoolArg("-acceptnonstdtxn", !Params().RequireStandard()); fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", Settings::DefaultPermitBareMultisig); fAcceptDatacarrier = GetBoolArg("-datacarrier", Settings::DefaultAcceptDataCarrier); nMaxDatacarrierBytes = GetArg("-datacarriersize", Settings::MaxOpReturnRelay); // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op if (GetBoolArg("-peerbloomfilters", true)) nLocalServices |= NODE_BLOOM; if (GetBoolArg("-use-thinblocks", false)) nLocalServices |= NODE_XTHIN; if (Params().NetworkIDString() == CBaseChainParams::MAIN) { if (Policy::blockSizeAcceptLimit() < 8000000) return InitError("The block size accept limit is too low, the minimum is 8MB. The Hub is shutting down."); if (GetArg("-blockmaxsize", Settings::DefaultBlockMAxSize) <= 1000000) return InitError("The maxblocksize mining limit is too low, it should be over 1MB. The Hub is shutting down."); } else if (Params().NetworkIDString() == CBaseChainParams::REGTEST) { // setup for testing to not use so much disk space. UnspentOutputDatabase::setSmallLimits(); } // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, hub log // Initialize elliptic curve code ECC_Start(); globalVerifyHandle.reset(new ECCVerifyHandle()); // Initialize SigCache InitSignatureCache(); // Sanity check if (!InitSanityCheck()) return InitError(_("Initialization sanity check failed. The Hub is shutting down.")); std::string strDataDir = GetDataDir().string(); // Make sure only a single Bitcoin process is using the data directory. boost::filesystem::path pathLockFile = GetDataDir() / ".lock"; FILE* file = fopen(pathLockFile.string().c_str(), "a"); // empty lock file; created if it doesn't exist. if (file) fclose(file); try { static boost::interprocess::file_lock lock(pathLockFile.string().c_str()); if (!lock.try_lock()) return InitError(strprintf(_("Cannot obtain a lock on data directory %s. The Hub is probably already running."), strDataDir)); } catch(const boost::interprocess::interprocess_exception& e) { return InitError(strprintf(_("Cannot obtain a lock on data directory %s. The Hub is probably already running.") + " %s.", strDataDir, e.what())); } #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif if (GetBoolArg("-shrinkdebugfile", true)) ShrinkDebugFile(); logCritical(Log::Bitcoin) << "Startup time:" << DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()); logCritical(Log::Bitcoin) << "Using data directory" << strDataDir; logCritical(Log::Bitcoin) << "Using config file" << GetConfigFile().string(); logCritical(Log::Bitcoin) << "Using log-config file" << GetConfigFile("logs.conf").string(); logInfo(Log::Net) << "Using at most" << nMaxConnections << "connections."; logInfo(Log::Internals) << nFD << "file descriptors available"; std::ostringstream strErrors; // Start the lightweight task scheduler thread CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(std::bind(&TraceThread, "scheduler", serviceLoop)); /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections * that the server is there and will be ready later). Warmup mode will * be disabled when initialisation is finished. */ if (fServer) { uiInterface.InitMessage.connect(SetRPCWarmupStatus); if (!AppInitServers()) return InitError(_("Unable to start HTTP server. See hub log for details.")); } Application::instance()->validation()->enableFeeResolveForMetaData(GetBoolArg("-feesmetadata", false)); Application::instance()->validation()->setMempool(&mempool); scheduler.scheduleEvery(std::bind(&DoubleSpendProofStorage::periodicCleanup, mempool.doubleSpendProofStorage()), 60); // ********************************************************* Step 5: load block chain bool fReindex = GetBoolArg("-reindex", false); bool fLoaded = false; int64_t nStart; while (!fLoaded) { bool fReset = fReindex; std::string strLoadError; uiInterface.InitMessage(_("Loading block index...")); nStart = GetTimeMillis(); do { try { UnloadBlockIndex(); delete g_utxo; g_utxo = nullptr; Blocks::DB::createInstance(40 << 20, fReindex, &scheduler); const auto utxoDir = GetDataDir() / "unspent"; if (fReindex) { boost::system::error_code error; // delete all files under unspent. Don't delete itself, it may be a symlink. boost::filesystem::directory_iterator iter(utxoDir, error); while (!error && iter != boost::filesystem::directory_iterator()) { boost::filesystem::remove_all(*iter, error); ++iter; } if (error) { // then try to delete the utxo dir itself. boost::filesystem::remove_all(utxoDir, error); if (error) { fRequestShutdown = true; logFatal(Log::Bitcoin) << "Can't remove the unspent dir to do a reindex" << error.message(); break; } } } g_utxo = new UnspentOutputDatabase(Application::instance()->ioContext(), utxoDir); mempool.setUtxo(g_utxo); if (fReindex) Blocks::DB::instance()->setReindexing(Blocks::ScanningFiles); if (!fReindex && !LoadBlockIndexDB(g_utxo)) { strLoadError = _("Error loading block database"); break; } Application::instance()->validation()->setBlockchain(&chainActive); while (!fReindex) { auto tip = Blocks::Index::get(g_utxo->blockId()); // now lets see if the utxo and the block-database like each other if (tip) { pindexBestHeader = Blocks::DB::instance()->headerChain().Tip(); chainActive.SetTip(tip); // Set the chain again after it received a Tip() Application::instance()->validation()->setBlockchain(&chainActive); // verify basics if (!VerifyDB().verifyDB(GetArg("-checklevel", Settings::DefaultCheckLevel), GetArg("-checkblocks", Settings::DefaultCheckBlocks))) { // verify failed, go to an older state. tip = nullptr; } } else if (g_utxo->blockId().IsNull()) { // first start. break; } else { logCritical(Log::Bitcoin) << "UTXO has tip which we don't have in the block-index. UTXO best block:" << g_utxo->blockId() << g_utxo->blockheight(); } if (tip) { // yes, lets go! logCritical(Log::Bitcoin) << "LoadBlockIndexDB: hashBestChain:" << tip->GetBlockHash() << "height:" << tip->nHeight << "nTx:" << tip->nChainTx << "date:" << DateTimeStrFormat("%Y-%m-%d %H:%M:%S", tip->GetBlockTime()).c_str() << "header height:" << Blocks::DB::instance()->headerChain().Height(); break; } else { // they don't like each other. Try an older UTXO state. if (!g_utxo->loadOlderState()) { fRequestShutdown = true; break; } } } if (fRequestShutdown) break; // Check whether we need to continue reindexing fReindex = fReindex || Blocks::DB::instance()->reindexing() != Blocks::NoReindex; // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). if (!Blocks::Index::empty() && !Blocks::Index::exists(chainparams.GetConsensus().hashGenesisBlock)) return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); // Initialize the block index (no-op if non-empty database was already loaded) if (!InitBlockIndex(chainparams)) { strLoadError = _("Error initializing block database"); break; } uiInterface.InitMessage(_("Verifying blocks...")); { LOCK(cs_main); CBlockIndex* tip = chainActive.Tip(); if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { strLoadError = _("The block database contains a block which appears to be from the future. " "This may be due to your computer's date and time being set incorrectly. " "Only rebuild the block database if you are sure that your computer's date and time are correct"); break; } } scheduler.scheduleEvery(std::bind(&UnspentOutputDatabase::saveCaches, g_utxo), 5 * 60); } catch (const std::exception& e) { logWarning() << e; strLoadError = _("Error opening block database"); break; } fLoaded = true; } while(false); if (fRequestShutdown) break; if (!fLoaded) { // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeMessageBox( strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; Blocks::DB::instance()->setReindexing(Blocks::ScanningFiles); fRequestShutdown = false; } else { logFatal(Log::Bitcoin) << "Aborted block database rebuild. Exiting."; return false; } } else { return InitError(strLoadError); } } } // As LoadBlockIndex can take several minutes, it's possible the user // requested to kill the GUI during the last operation. If so, exit. // As the program has not fully started yet, Shutdown() is possibly overkill. if (fRequestShutdown) { logFatal(Log::Bitcoin) << "Shutdown requested. Exiting."; return false; } logInfo(Log::Bench).nospace() << "block index load took: " << GetTimeMillis() - nStart << "ms"; if (Params().NetworkIDString() != CBaseChainParams::REGTEST) { // For non-testing setups, keep tracks of disk-space free stats. auto &dsc = Application::instance()->diskSpaceChecker(); if (!dsc.enoughSpaceAvailable()) return InitError("Not enough disk space available to safely start"); dsc.start(); // keep checking } // ********************************************************* Step 6: network initialization CTxOrphanCache::instance()->setLimit((unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", Settings::DefaultMaxOrphanTransactions))); RegisterNodeSignals(GetNodeSignals()); if (mapArgs.count("-onlynet")) { std::set nets; for (const std::string& snet : mapMultiArgs["-onlynet"]) { CNetAddr::Network net = ParseNetwork(snet); if (net == CNetAddr::NET_UNROUTABLE) return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); nets.insert(net); } for (int n = 0; n < CNetAddr::NET_MAX; n++) { CNetAddr::Network net = (CNetAddr::Network)n; if (!nets.count(net)) SetLimited(net); } } if (mapArgs.count("-whitelist")) { for (const std::string& net : mapMultiArgs["-whitelist"]) { CSubNet subnet(net); if (!subnet.IsValid()) return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); CNode::AddWhitelistedRange(subnet); } } bool proxyRandomize = GetBoolArg("-proxyrandomize", Settings::DefaultProxyRandomize); // -proxy sets a proxy for all outgoing network traffic // -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default std::string proxyArg = GetArg("-proxy", ""); SetLimited(CNetAddr::NET_TOR); if (proxyArg != "" && proxyArg != "0") { proxyType addrProxy = proxyType(CService(proxyArg, 9050), proxyRandomize); if (!addrProxy.IsValid()) return InitError(strprintf(_("Invalid -proxy address: '%s'"), proxyArg)); SetProxy(CNetAddr::NET_IPV4, addrProxy); SetProxy(CNetAddr::NET_IPV6, addrProxy); SetProxy(CNetAddr::NET_TOR, addrProxy); SetNameProxy(addrProxy); SetLimited(CNetAddr::NET_TOR, false); // by default, -proxy sets onion as reachable, unless -noonion later } // -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses // -noonion (or -onion=0) disables connecting to .onion entirely // An empty string is used to not override the onion proxy (in which case it defaults to -proxy set above, or none) std::string onionArg = GetArg("-onion", ""); if (onionArg != "") { if (onionArg == "0") { // Handle -noonion/-onion=0 SetLimited(CNetAddr::NET_TOR); // set onions as unreachable } else { proxyType addrOnion = proxyType(CService(onionArg, 9050), proxyRandomize); if (!addrOnion.IsValid()) return InitError(strprintf(_("Invalid -onion address: '%s'"), onionArg)); SetProxy(CNetAddr::NET_TOR, addrOnion); SetLimited(CNetAddr::NET_TOR, false); } } // see Step 2: parameter interactions for more information about these fListen = GetBoolArg("-listen", DEFAULT_LISTEN); fDiscover = GetBoolArg("-discover", true); fNameLookup = GetBoolArg("-dns", Settings::DefaultNameLookup); bool fBound = false; if (fListen) { if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { for (const std::string& strBind : mapMultiArgs["-bind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false)) return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } for (const std::string& strBind : mapMultiArgs["-whitebind"]) { CService addrBind; if (!Lookup(strBind.c_str(), addrBind, 0, false)) return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind)); if (addrBind.GetPort() == 0) return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind)); fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST)); } } else { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; fBound |= Bind(CService(in6addr_any, GetListenPort()), BF_NONE); fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } if (!fBound) return InitError(_("Failed to listen on any port. Use -listen=0 if you want this.")); } if (mapArgs.count("-externalip")) { for (const std::string& strAddr : mapMultiArgs["-externalip"]) { CService addrLocal(strAddr, GetListenPort(), fNameLookup); if (!addrLocal.IsValid()) return InitError(strprintf(_("Cannot resolve -externalip address: '%s'"), strAddr)); AddLocal(CService(strAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL); } } for (const std::string& strDest : mapMultiArgs["-seednode"]) AddOneShot(strDest); #if ENABLE_ZMQ pzmqNotificationInterface = CZMQNotificationInterface::CreateWithArguments(mapArgs); if (pzmqNotificationInterface) { ValidationNotifier().addListener(pzmqNotificationInterface); } #endif if (mapArgs.count("-maxuploadtarget")) { CNode::SetMaxOutboundTarget(GetArg("-maxuploadtarget", Settings::DefaultMaxUploadTarget)*1024*1024); } // ********************************************************* Step 7: import blocks if (mapArgs.count("-blocknotify")) uiInterface.NotifyBlockTip.connect(BlockNotifyCallback); Blocks::DB::startBlockImporter(); if (chainActive.Tip() == nullptr) { logDebug(Log::Bitcoin) << "Waiting for genesis block to be imported..."; while (!fRequestShutdown && chainActive.Tip() == nullptr) MilliSleep(10); } Application::instance()->validation()->start(); // ********************************************************* Step 8: start node if (!CheckDiskSpace()) return false; if (!strErrors.str().empty()) return InitError(strErrors.str()); RandAddSeedPerfmon(); //// debug print logDebug(Log::DB) << "mapBlockIndex.size() =" << Blocks::Index::size(); logDebug(Log::BlockValidation) << "nBestHeight =" << chainActive.Height(); if (GetBoolArg("-listenonion", Settings::DefaultListenOnion)) StartTorControl(threadGroup, scheduler); StartNode(threadGroup, scheduler); // Monitor the chain, and alert if we get blocks much quicker or slower than expected const int64_t nPowTargetSpacing = Params().GetConsensus().nPowTargetSpacing; scheduler.scheduleEvery(std::bind(&Blocks::DB::partitionCheck, Blocks::DB::instance(), nPowTargetSpacing), nPowTargetSpacing); // Generate coins in the background try { Mining::GenerateBitcoins(GetBoolArg("-gen", Settings::DefaultGenerateCoins), GetArg("-genproclimit", Settings::DefaultGenerateThreads), chainparams, GetArg("-gencoinbase", "")); } catch (const std::exception &e) { logCritical(Log::Bitcoin) << "Mining could not be activated. Reason:" << e; } // ********************************************************* Step 9: finished SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); return !fRequestShutdown; }