0ae11a2bfc
Since we activated op-mul at the 64-bit upgrade date, this test now follows this idea.
1025 lines
40 KiB
C++
1025 lines
40 KiB
C++
/*
|
|
* 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 <tom@flowee.org>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config/flowee-config.h"
|
|
#endif
|
|
|
|
#include "init.h"
|
|
#include <SettingsDefaults.h>
|
|
|
|
#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 <primitives/PrivateKey.h>
|
|
#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 <util.h>
|
|
#include <utiltime.h>
|
|
#include "serverutil.h"
|
|
#include <utilmoneystr.h>
|
|
#include <utilstrencodings.h>
|
|
#include "txorphancache.h"
|
|
#include "validationinterface.h"
|
|
#include <validation/Engine.h>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
|
|
#ifndef WIN32
|
|
#include <csignal>
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include <validation/VerifyDB.h>
|
|
#include <utxo/UnspentOutputDatabase.h>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/function.hpp>
|
|
#include <boost/interprocess/sync/file_lock.hpp>
|
|
#include <boost/thread.hpp>
|
|
#include <openssl/crypto.h>
|
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
|
|
|
|
#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 <char> 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<ECCVerifyHandle> 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<std::string>((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<std::string>(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=<amount>: '%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<CScheduler::Function>, "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<CNetAddr::Network> 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;
|
|
}
|