diff --git a/src/bitcoindjs.cc b/src/bitcoindjs.cc index a9eb8a86..82cf38de 100644 --- a/src/bitcoindjs.cc +++ b/src/bitcoindjs.cc @@ -294,6 +294,66 @@ start_node(void) { * ~/bitcoin/src/net.cpp (StartNode() - starts the network threads) */ + int nBind = std::max((int)mapArgs.count("-bind"), 1); + nMaxConnections = GetArg("-maxconnections", 125); + nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); + int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS); + if (nFD < MIN_CORE_FILEDESCRIPTORS) + return InitError(_("Not enough file descriptors available.")); + if (nFD - MIN_CORE_FILEDESCRIPTORS < nMaxConnections) + nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; + fDebug = !mapMultiArgs["-debug"].empty(); + fBenchmark = GetBoolArg("-benchmark", false); + mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest())); + Checkpoints::fEnabled = GetBoolArg("-checkpoints", true); + + nScriptCheckThreads = GetArg("-par", 0); + if (nScriptCheckThreads <= 0) + nScriptCheckThreads += boost::thread::hardware_concurrency(); + if (nScriptCheckThreads <= 1) + nScriptCheckThreads = 0; + else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS) + nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS; + + fServer = GetBoolArg("-server", false); + fPrintToConsole = GetBoolArg("-printtoconsole", false); + fLogTimestamps = GetBoolArg("-logtimestamps", true); + +#ifdef ENABLE_WALLET + bool fDisableWallet = GetBoolArg("-disablewallet", false); +#endif + + if (mapArgs.count("-timeout")) + { + int nNewTimeout = GetArg("-timeout", 5000); + if (nNewTimeout > 0 && nNewTimeout < 600000) + nConnectTimeout = nNewTimeout; + } + + // Continue to put "/P2SH/" in the coinbase to monitor + // BIP16 support. + // This can be removed eventually... + const char* pszP2SH = "/P2SH/"; + COINBASE_FLAGS << std::vector(pszP2SH, pszP2SH+strlen(pszP2SH)); + + + + strWalletFile = GetArg("-wallet", "wallet.dat"); + std::string strDataDir = GetDataDir().string(); + if (strWalletFile != boost::filesystem::basename(strWalletFile) + boost::filesystem::extension(strWalletFile)) + return InitError(strprintf(_("Wallet %s resides outside data directory %s"), strWalletFile, strDataDir)); + + + 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); + 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. Bitcoin is probably already running."), strDataDir)); + + + if (GetBoolArg("-shrinkdebugfile", !fDebug)) + ShrinkDebugFile(); // XXX Run this in a node thread instead to keep the event loop open: // boost::thread_group threadGroup; // boost::thread *detectShutdownThread = NULL; @@ -302,6 +362,351 @@ start_node(void) { // for (int i = 0; i < nScriptCheckThreads - 1; i++) { // threadGroup.create_thread(&ThreadScriptCheck); // } + +#ifdef ENABLE_WALLET + if (!fDisableWallet) { + LogPrintf("Using wallet %s\n", strWalletFile); + uiInterface.InitMessage(_("Verifying wallet...")); + + if (!bitdb.Open(GetDataDir())) + { + // try moving the database env out of the way + boost::filesystem::path pathDatabase = GetDataDir() / "database"; + boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); + try { + boost::filesystem::rename(pathDatabase, pathDatabaseBak); + LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); + } catch(boost::filesystem::filesystem_error &error) { + // failure is ok (well, not really, but it's not worse than what we started with) + } + + // try again + if (!bitdb.Open(GetDataDir())) { + // if it still fails, it probably means we can't even create the database env + string msg = strprintf(_("Error initializing wallet database environment %s!"), strDataDir); + return InitError(msg); + } + } + + if (GetBoolArg("-salvagewallet", false)) + { + // Recover readable keypairs: + if (!CWalletDB::Recover(bitdb, strWalletFile, true)) + return false; + } + + if (filesystem::exists(GetDataDir() / strWalletFile)) + { + CDBEnv::VerifyResult r = bitdb.Verify(strWalletFile, CWalletDB::Recover); + if (r == CDBEnv::RECOVER_OK) + { + string msg = strprintf(_("Warning: wallet.dat corrupt, data salvaged!" + " Original wallet.dat saved as wallet.{timestamp}.bak in %s; if" + " your balance or transactions are incorrect you should" + " restore from a backup."), strDataDir); + InitWarning(msg); + } + if (r == CDBEnv::RECOVER_FAIL) + return InitError(_("wallet.dat corrupt, salvage failed")); + } + } // (!fDisableWallet) +#endif // ENABLE_WALLET + // ********************************************************* Step 6: network initialization + + + BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"]) + AddOneShot(strDest); + fReindex = GetBoolArg("-reindex", false); + + // Upgrading to 0.8; hard-link the old blknnnn.dat files into /blocks/ + filesystem::path blocksDir = GetDataDir() / "blocks"; + if (!filesystem::exists(blocksDir)) + { + filesystem::create_directories(blocksDir); + bool linked = false; + for (unsigned int i = 1; i < 10000; i++) { + filesystem::path source = GetDataDir() / strprintf("blk%04u.dat", i); + if (!filesystem::exists(source)) break; + filesystem::path dest = blocksDir / strprintf("blk%05u.dat", i-1); + try { + filesystem::create_hard_link(source, dest); + LogPrintf("Hardlinked %s -> %s\n", source.string(), dest.string()); + linked = true; + } catch (filesystem::filesystem_error & e) { + // Note: hardlink creation failing is not a disaster, it just means + // blocks will get re-downloaded from peers. + LogPrintf("Error hardlinking blk%04u.dat : %s\n", i, e.what()); + break; + } + } + if (linked) + { + fReindex = true; + } + } + + // cache size calculations + size_t nTotalCache = (GetArg("-dbcache", nDefaultDbCache) << 20); + if (nTotalCache < (nMinDbCache << 20)) + nTotalCache = (nMinDbCache << 20); // total cache cannot be less than nMinDbCache + else if (nTotalCache > (nMaxDbCache << 20)) + nTotalCache = (nMaxDbCache << 20); // total cache cannot be greater than nMaxDbCache + size_t nBlockTreeDBCache = nTotalCache / 8; + if (nBlockTreeDBCache > (1 << 21) && !GetBoolArg("-txindex", false)) + nBlockTreeDBCache = (1 << 21); // block tree db cache shouldn't be larger than 2 MiB + nTotalCache -= nBlockTreeDBCache; + size_t nCoinDBCache = nTotalCache / 2; // use half of the remaining cache for coindb cache + nTotalCache -= nCoinDBCache; + nCoinCacheSize = nTotalCache / 300; // coins in memory require around 300 bytes + + bool fLoaded = false; + while (!fLoaded) { + bool fReset = fReindex; + std::string strLoadError; + + uiInterface.InitMessage(_("Loading block index...")); + + nStart = GetTimeMillis(); + do { + try { + UnloadBlockIndex(); + delete pcoinsTip; + delete pcoinsdbview; + delete pblocktree; + + pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); + pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); + pcoinsTip = new CCoinsViewCache(*pcoinsdbview); + + if (fReindex) + pblocktree->WriteReindexing(true); + + if (!LoadBlockIndex()) { + strLoadError = _("Error loading block database"); + break; + } + + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). + if (!mapBlockIndex.empty() && chainActive.Genesis() == NULL) + 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()) { + strLoadError = _("Error initializing block database"); + break; + } + + // Check for changed -txindex state + if (fTxIndex != GetBoolArg("-txindex", false)) { + strLoadError = _("You need to rebuild the database using -reindex to change -txindex"); + break; + } + + uiInterface.InitMessage(_("Verifying blocks...")); + if (!VerifyDB(GetArg("-checklevel", 3), + GetArg("-checkblocks", 288))) { + strLoadError = _("Corrupted block database detected"); + break; + } + } catch(std::exception &e) { + if (fDebug) LogPrintf("%s\n", e.what()); + strLoadError = _("Error opening block database"); + break; + } + + fLoaded = true; + } while(false); + + 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; + fRequestShutdown = false; + } else { + LogPrintf("Aborted block database rebuild. Exiting.\n"); + 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) + { + LogPrintf("Shutdown requested. Exiting.\n"); + return false; + } + + // ********************************************************* Step 8: load wallet +#ifdef ENABLE_WALLET + if (fDisableWallet) { + pwalletMain = NULL; + LogPrintf("Wallet disabled!\n"); + } else { + if (GetBoolArg("-zapwallettxes", false)) { + uiInterface.InitMessage(_("Zapping all transactions from wallet...")); + + pwalletMain = new CWallet(strWalletFile); + DBErrors nZapWalletRet = pwalletMain->ZapWalletTx(); + if (nZapWalletRet != DB_LOAD_OK) { + uiInterface.InitMessage(_("Error loading wallet.dat: Wallet corrupted")); + return false; + } + + delete pwalletMain; + pwalletMain = NULL; + } + + uiInterface.InitMessage(_("Loading wallet...")); + + nStart = GetTimeMillis(); + bool fFirstRun = true; + pwalletMain = new CWallet(strWalletFile); + DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun); + if (nLoadWalletRet != DB_LOAD_OK) + { + if (nLoadWalletRet == DB_CORRUPT) + strErrors << _("Error loading wallet.dat: Wallet corrupted") << "\n"; + else if (nLoadWalletRet == DB_NONCRITICAL_ERROR) + { + string msg(_("Warning: error reading wallet.dat! All keys read correctly, but transaction data" + " or address book entries might be missing or incorrect.")); + InitWarning(msg); + } + else if (nLoadWalletRet == DB_TOO_NEW) + strErrors << _("Error loading wallet.dat: Wallet requires newer version of Bitcoin") << "\n"; + else if (nLoadWalletRet == DB_NEED_REWRITE) + { + strErrors << _("Wallet needed to be rewritten: restart Bitcoin to complete") << "\n"; + LogPrintf("%s", strErrors.str()); + return InitError(strErrors.str()); + } + else + strErrors << _("Error loading wallet.dat") << "\n"; + } + + if (GetBoolArg("-upgradewallet", fFirstRun)) + { + int nMaxVersion = GetArg("-upgradewallet", 0); + if (nMaxVersion == 0) // the -upgradewallet without argument case + { + LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST); + nMaxVersion = CLIENT_VERSION; + pwalletMain->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately + } + else + LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion); + if (nMaxVersion < pwalletMain->GetVersion()) + strErrors << _("Cannot downgrade wallet") << "\n"; + pwalletMain->SetMaxVersion(nMaxVersion); + } + + if (fFirstRun) + { + // Create new keyUser and set as default key + RandAddSeedPerfmon(); + + CPubKey newDefaultKey; + if (pwalletMain->GetKeyFromPool(newDefaultKey)) { + pwalletMain->SetDefaultKey(newDefaultKey); + if (!pwalletMain->SetAddressBook(pwalletMain->vchDefaultKey.GetID(), "", "receive")) + strErrors << _("Cannot write default address") << "\n"; + } + + pwalletMain->SetBestChain(chainActive.GetLocator()); + } + + LogPrintf("%s", strErrors.str()); + LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); + + RegisterWallet(pwalletMain); + + CBlockIndex *pindexRescan = chainActive.Tip(); + if (GetBoolArg("-rescan", false)) + pindexRescan = chainActive.Genesis(); + else + { + CWalletDB walletdb(strWalletFile); + CBlockLocator locator; + if (walletdb.ReadBestBlock(locator)) + pindexRescan = chainActive.FindFork(locator); + else + pindexRescan = chainActive.Genesis(); + } + 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(); + pwalletMain->ScanForWalletTransactions(pindexRescan, true); + LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart); + pwalletMain->SetBestChain(chainActive.GetLocator()); + nWalletDBUpdated++; + } + } // (!fDisableWallet) +#else // ENABLE_WALLET + LogPrintf("No wallet compiled in!\n"); +#endif // !ENABLE_WALLET + // ********************************************************* Step 9: import blocks + + // scan for better chains in the block chain database, that are not yet connected in the active best chain + CValidationState state; + if (!ActivateBestChain(state)) + strErrors << "Failed to connect best block"; + + std::vector vImportFiles; + if (mapArgs.count("-loadblock")) + { + BOOST_FOREACH(string strFile, mapMultiArgs["-loadblock"]) + vImportFiles.push_back(strFile); + } + threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); + + // ********************************************************* Step 10: load peers + + uiInterface.InitMessage(_("Loading addresses...")); + + nStart = GetTimeMillis(); + + { + CAddrDB adb; + if (!adb.Read(addrman)) + LogPrintf("Invalid or missing peers.dat; recreating\n"); + } + + LogPrintf("Loaded %i addresses from peers.dat %dms\n", + addrman.size(), GetTimeMillis() - nStart); + + // ********************************************************* Step 11: start node + + if (!CheckDiskSpace()) + return false; + + if (!strErrors.str().empty()) + return InitError(strErrors.str()); + + RandAddSeedPerfmon(); + + //// debug print + LogPrintf("mapBlockIndex.size() = %"PRIszu"\n", mapBlockIndex.size()); + LogPrintf("nBestHeight = %d\n", chainActive.Height()); +#ifdef ENABLE_WALLET + LogPrintf("setKeyPool.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->setKeyPool.size() : 0); + LogPrintf("mapWallet.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapWallet.size() : 0); + LogPrintf("mapAddressBook.size() = %"PRIszu"\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); +#endif + + + // std::vector vImportFiles; // threadGroup.create_thread(boost::bind(&ThreadImport, vImportFiles)); // StartNode(threadGroup); @@ -311,6 +716,34 @@ start_node(void) { // threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); // } #endif + + // InitRPCMining is needed here so getwork/getblocktemplate in the GUI debug console works properly. + InitRPCMining(); + if (fServer) + StartRPCThreads(); + +#ifdef ENABLE_WALLET + // Generate coins in the background + if (pwalletMain) + GenerateBitcoins(GetBoolArg("-gen", false), pwalletMain, GetArg("-genproclimit", -1)); +#endif + + // ********************************************************* Step 12: finished + + uiInterface.InitMessage(_("Done loading")); + +#ifdef ENABLE_WALLET + if (pwalletMain) { + // Add wallet transactions that aren't already in a block to mapTransactions + pwalletMain->ReacceptWalletTransactions(); + + // Run a thread to flush wallet periodically + threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(pwalletMain->strWalletFile))); + } +#endif + + return !fRequestShutdown; + // return 0; /**