diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/chainparams.cpp | 1 | ||||
-rw-r--r-- | src/init.cpp | 66 | ||||
-rw-r--r-- | src/leveldbwrapper.cpp | 5 | ||||
-rw-r--r-- | src/main.cpp | 24 | ||||
-rw-r--r-- | src/main.h | 2 | ||||
-rw-r--r-- | src/miner.cpp | 3 | ||||
-rw-r--r-- | src/net.cpp | 60 | ||||
-rw-r--r-- | src/net.h | 13 | ||||
-rw-r--r-- | src/qt/bitcoingui.cpp | 25 | ||||
-rw-r--r-- | src/qt/rpcconsole.cpp | 29 | ||||
-rw-r--r-- | src/qt/rpcconsole.h | 2 | ||||
-rw-r--r-- | src/rpcblockchain.cpp | 4 | ||||
-rw-r--r-- | src/rpcmining.cpp | 60 | ||||
-rw-r--r-- | src/rpcnet.cpp | 1 | ||||
-rw-r--r-- | src/rpcrawtransaction.cpp | 2 | ||||
-rw-r--r-- | src/rpcserver.cpp | 49 | ||||
-rw-r--r-- | src/rpcserver.h | 2 | ||||
-rw-r--r-- | src/rpcwallet.cpp | 2 | ||||
-rw-r--r-- | src/sync.h | 3 | ||||
-rw-r--r-- | src/ui_interface.h | 2 | ||||
-rw-r--r-- | src/wallet.cpp | 12 |
21 files changed, 275 insertions, 92 deletions
diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 50977dc06b..fb1d05f833 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -152,6 +152,7 @@ public: vSeeds.push_back(CDNSSeedData("dashjr.org", "dnsseed.bitcoin.dashjr.org")); vSeeds.push_back(CDNSSeedData("bitcoinstats.com", "seed.bitcoinstats.com")); vSeeds.push_back(CDNSSeedData("bitnodes.io", "seed.bitnodes.io")); + vSeeds.push_back(CDNSSeedData("open-nodes.org", "seeds.bitcoin.open-nodes.org")); vSeeds.push_back(CDNSSeedData("xf2.org", "bitseed.xf2.org")); base58Prefixes[PUBKEY_ADDRESS] = list_of(0); diff --git a/src/init.cpp b/src/init.cpp index f9e568a207..99df237b2a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -38,8 +38,8 @@ #include <boost/interprocess/sync/file_lock.hpp> #include <openssl/crypto.h> -using namespace std; using namespace boost; +using namespace std; #ifdef ENABLE_WALLET CWallet* pwalletMain; @@ -58,7 +58,8 @@ CWallet* pwalletMain; enum BindFlags { BF_NONE = 0, BF_EXPLICIT = (1U << 0), - BF_REPORT_ERROR = (1U << 1) + BF_REPORT_ERROR = (1U << 1), + BF_WHITELIST = (1U << 2), }; static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; @@ -109,10 +110,11 @@ static CCoinsViewDB *pcoinsdbview; void Shutdown() { - LogPrintf("Shutdown : In progress...\n"); + LogPrintf("%s: In progress...\n", __func__); static CCriticalSection cs_Shutdown; TRY_LOCK(cs_Shutdown, lockShutdown); - if (!lockShutdown) return; + if (!lockShutdown) + return; RenameThread("bitcoin-shutoff"); mempool.AddTransactionsUpdated(1); @@ -130,7 +132,7 @@ void Shutdown() if (est_fileout) mempool.WriteFeeEstimates(est_fileout); else - LogPrintf("failed to write fee estimates"); + LogPrintf("%s: Failed to write fee estimates to %s\n", __func__, est_path.string()); { LOCK(cs_main); @@ -142,9 +144,12 @@ void Shutdown() pblocktree->Flush(); if (pcoinsTip) pcoinsTip->Flush(); - delete pcoinsTip; pcoinsTip = NULL; - delete pcoinsdbview; pcoinsdbview = NULL; - delete pblocktree; pblocktree = NULL; + delete pcoinsTip; + pcoinsTip = NULL; + delete pcoinsdbview; + pcoinsdbview = NULL; + delete pblocktree; + pblocktree = NULL; } #ifdef ENABLE_WALLET if (pwalletMain) @@ -156,7 +161,7 @@ void Shutdown() if (pwalletMain) delete pwalletMain; #endif - LogPrintf("Shutdown : done\n"); + LogPrintf("%s: done\n", __func__); } // @@ -174,13 +179,13 @@ void HandleSIGHUP(int) bool static InitError(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::NOSHOWGUI); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; } bool static InitWarning(const std::string &str) { - uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING | CClientUIInterface::NOSHOWGUI); + uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); return true; } @@ -188,7 +193,7 @@ bool static Bind(const CService &addr, unsigned int flags) { if (!(flags & BF_EXPLICIT) && IsLimited(addr)) return false; std::string strError; - if (!BindListenPort(addr, strError)) { + if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) { if (flags & BF_REPORT_ERROR) return InitError(strError); return false; @@ -214,7 +219,6 @@ std::string HelpMessage(HelpMessageMode mode) } strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n"; - strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n"; strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n"; strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n"; @@ -249,10 +253,13 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n"; #endif #endif + strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n"; + strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n"; #ifdef ENABLE_WALLET strUsage += "\n" + _("Wallet options:") + "\n"; strUsage += " -disablewallet " + _("Do not load the wallet and disable wallet RPC calls") + "\n"; + strUsage += " -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n"; if (GetBoolArg("-help-debug", false)) strUsage += " -mintxfee=<amt> " + strprintf(_("Fees (in BTC/Kb) smaller than this are considered zero fee for transaction creation (default: %s)"), FormatMoney(CWallet::minTxFee.GetFeePerK())) + "\n"; strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n"; @@ -288,8 +295,10 @@ std::string HelpMessage(HelpMessageMode mode) if (mode == HMM_BITCOIN_QT) strUsage += ", qt"; strUsage += ".\n"; +#ifdef ENABLE_WALLET strUsage += " -gen " + _("Generate coins (default: 0)") + "\n"; strUsage += " -genproclimit=<n> " + _("Set the processor limit for when generation is on (-1 = unlimited, default: -1)") + "\n"; +#endif strUsage += " -help-debug " + _("Show all debugging options (usage: --help -help-debug)") + "\n"; strUsage += " -logips " + _("Include IP addresses in debug output (default: 0)") + "\n"; strUsage += " -logtimestamps " + _("Prepend debug output with timestamp (default: 1)") + "\n"; @@ -315,6 +324,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += "\n" + _("Node relay options:") + "\n"; strUsage += " -datacarrier " + _("Relay and mine data carrier transactions (default: 1)") + "\n"; + strUsage += "\n" + _("Block creation options:") + "\n"; strUsage += " -blockminsize=<n> " + _("Set minimum block size in bytes (default: 0)") + "\n"; strUsage += " -blockmaxsize=<n> " + strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE) + "\n"; @@ -499,11 +509,11 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 2: parameter interactions - if (mapArgs.count("-bind")) { + if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { // when specifying an explicit binding address, you want to listen on it // even when -connect or -proxy is specified if (SoftSetBoolArg("-listen", true)) - LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n"); + LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n"); } if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) { @@ -547,7 +557,7 @@ bool AppInit2(boost::thread_group& threadGroup) } // Make sure enough file descriptors are available - int nBind = std::max((int)mapArgs.count("-bind"), 1); + int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 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); @@ -564,9 +574,9 @@ bool AppInit2(boost::thread_group& threadGroup) if (GetBoolArg("-nodebug", false) || find(categories.begin(), categories.end(), string("0")) != categories.end()) fDebug = false; - // Check for -debugnet (deprecated) + // Check for -debugnet if (GetBoolArg("-debugnet", false)) - InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net")); + InitWarning(_("Warning: Unsupported argument -debugnet ignored, use -debug=net.")); // Check for -socks - as this is a privacy risk to continue, exit here if (mapArgs.count("-socks")) return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); @@ -764,6 +774,15 @@ bool AppInit2(boost::thread_group& threadGroup) } } + if (mapArgs.count("-whitelist")) { + BOOST_FOREACH(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); + } + } + CService addrProxy; bool fProxy = false; if (mapArgs.count("-proxy")) { @@ -800,13 +819,21 @@ bool AppInit2(boost::thread_group& threadGroup) bool fBound = false; if (fListen) { - if (mapArgs.count("-bind")) { + if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) { BOOST_FOREACH(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)); } + BOOST_FOREACH(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; @@ -994,6 +1021,7 @@ bool AppInit2(boost::thread_group& threadGroup) boost::filesystem::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein = CAutoFile(fopen(est_path.string().c_str(), "rb"), SER_DISK, CLIENT_VERSION); + // Allowed to fail as this file IS missing on first startup. if (est_filein) mempool.ReadFeeEstimates(est_filein); diff --git a/src/leveldbwrapper.cpp b/src/leveldbwrapper.cpp index 5b4a9c147b..9e849696a8 100644 --- a/src/leveldbwrapper.cpp +++ b/src/leveldbwrapper.cpp @@ -32,6 +32,11 @@ static leveldb::Options GetOptions(size_t nCacheSize) { options.filter_policy = leveldb::NewBloomFilterPolicy(10); options.compression = leveldb::kNoCompression; options.max_open_files = 64; + if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { + // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error + // on corruption in later versions. + options.paranoid_checks = true; + } return options; } diff --git a/src/main.cpp b/src/main.cpp index 8225d73d8d..4f6b442f3a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,8 @@ CCriticalSection cs_main; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; int64_t nTimeBestReceived = 0; +CWaitableCriticalSection csBestBlock; +CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; bool fImporting = false; bool fReindex = false; @@ -210,7 +212,7 @@ struct CBlockReject { struct CNodeState { // Accumulated misbehaviour score for this peer. int nMisbehavior; - // Whether this peer should be disconnected and banned. + // Whether this peer should be disconnected and banned (unless whitelisted). bool fShouldBan; // String name of this peer (debugging/logging purposes). std::string name; @@ -1425,7 +1427,8 @@ void Misbehaving(NodeId pnode, int howmuch) return; state->nMisbehavior += howmuch; - if (state->nMisbehavior >= GetArg("-banscore", 100)) + int banscore = GetArg("-banscore", 100); + if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore) { LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior); state->fShouldBan = true; @@ -1944,11 +1947,14 @@ void static UpdateTip(CBlockIndex *pindexNew) { // New best block nTimeBestReceived = GetTime(); mempool.AddTransactionsUpdated(1); + LogPrintf("UpdateTip: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)chainActive.Tip()->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()), Checkpoints::GuessVerificationProgress(chainActive.Tip())); + cvBlockChange.notify_all(); + // Check the version of the last 100 blocks to see if we need to upgrade: if (!fIsInitialDownload) { @@ -3947,6 +3953,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS); if (nEvicted > 0) LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); + } else if (pfrom->fWhitelisted) { + // Always relay transactions received from whitelisted peers, even + // if they are already in the mempool (allowing the node to function + // as a gateway for nodes hidden behind it). + RelayTransaction(tx); } int nDoS = 0; if (state.IsInvalid(nDoS)) @@ -4440,11 +4451,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle) CNodeState &state = *State(pto->GetId()); if (state.fShouldBan) { - if (pto->addr.IsLocal()) - LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString()); + if (pto->fWhitelisted) + LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString()); else { pto->fDisconnect = true; - CNode::Ban(pto->addr); + if (pto->addr.IsLocal()) + LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); + else + CNode::Ban(pto->addr); } state.fShouldBan = false; } diff --git a/src/main.h b/src/main.h index f6bac889be..a683412571 100644 --- a/src/main.h +++ b/src/main.h @@ -87,6 +87,8 @@ extern uint64_t nLastBlockTx; extern uint64_t nLastBlockSize; extern const std::string strMessageMagic; extern int64_t nTimeBestReceived; +extern CWaitableCriticalSection csBestBlock; +extern CConditionVariable cvBlockChange; extern bool fImporting; extern bool fReindex; extern bool fBenchmark; diff --git a/src/miner.cpp b/src/miner.cpp index 17918a1280..ec56c71192 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -467,7 +467,10 @@ void static BitcoinMiner(CWallet *pwallet) auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey)); if (!pblocktemplate.get()) + { + LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n"); return; + } CBlock *pblock = &pblocktemplate->block; IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); diff --git a/src/net.cpp b/src/net.cpp index 0e663aea83..3b3d91d652 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -50,7 +50,16 @@ using namespace std; using namespace boost; -static const int MAX_OUTBOUND_CONNECTIONS = 8; +namespace { + const int MAX_OUTBOUND_CONNECTIONS = 8; + + struct ListenSocket { + SOCKET socket; + bool whitelisted; + + ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} + }; +} // // Global state variables @@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; static CNode* pnodeSync = NULL; uint64_t nLocalHostNonce = 0; -static std::vector<SOCKET> vhListenSocket; +static std::vector<ListenSocket> vhListenSocket; CAddrMan addrman; int nMaxConnections = 125; @@ -593,6 +602,24 @@ bool CNode::Ban(const CNetAddr &addr) { return true; } + +std::vector<CSubNet> CNode::vWhitelistedRange; +CCriticalSection CNode::cs_vWhitelistedRange; + +bool CNode::IsWhitelistedRange(const CNetAddr &addr) { + LOCK(cs_vWhitelistedRange); + BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { + if (subnet.Match(addr)) + return true; + } + return false; +} + +void CNode::AddWhitelistedRange(const CSubNet &subnet) { + LOCK(cs_vWhitelistedRange); + vWhitelistedRange.push_back(subnet); +} + #undef X #define X(name) stats.name = name void CNode::copyStats(CNodeStats &stats) @@ -609,6 +636,7 @@ void CNode::copyStats(CNodeStats &stats) X(nStartingHeight); X(nSendBytes); X(nRecvBytes); + X(fWhitelisted); stats.fSyncNode = (this == pnodeSync); // It is common for nodes with good ping times to suddenly become lagged, @@ -848,9 +876,9 @@ void ThreadSocketHandler() SOCKET hSocketMax = 0; bool have_fds = false; - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) { - FD_SET(hListenSocket, &fdsetRecv); - hSocketMax = max(hSocketMax, hListenSocket); + BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { + FD_SET(hListenSocket.socket, &fdsetRecv); + hSocketMax = max(hSocketMax, hListenSocket.socket); have_fds = true; } @@ -917,13 +945,13 @@ void ThreadSocketHandler() // // Accept new connections // - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) + BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) { - if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv)) + if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv)) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); - SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); + SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; @@ -931,6 +959,7 @@ void ThreadSocketHandler() if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) LogPrintf("Warning: Unknown socket family\n"); + bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -948,7 +977,7 @@ void ThreadSocketHandler() { closesocket(hSocket); } - else if (CNode::IsBanned(addr)) + else if (CNode::IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); closesocket(hSocket); @@ -957,6 +986,7 @@ void ThreadSocketHandler() { CNode* pnode = new CNode(hSocket, addr, "", true); pnode->AddRef(); + pnode->fWhitelisted = whitelisted; { LOCK(cs_vNodes); @@ -1580,7 +1610,7 @@ void ThreadMessageHandler() -bool BindListenPort(const CService &addrBind, string& strError) +bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; @@ -1661,9 +1691,9 @@ bool BindListenPort(const CService &addrBind, string& strError) return false; } - vhListenSocket.push_back(hListenSocket); + vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted)); - if (addrBind.IsRoutable() && fDiscover) + if (addrBind.IsRoutable() && fDiscover && !fWhitelisted) AddLocal(addrBind, LOCAL_BIND); return true; @@ -1788,9 +1818,9 @@ public: BOOST_FOREACH(CNode* pnode, vNodes) if (pnode->hSocket != INVALID_SOCKET) closesocket(pnode->hSocket); - BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) - if (hListenSocket != INVALID_SOCKET) - if (closesocket(hListenSocket) == SOCKET_ERROR) + BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) + if (hListenSocket.socket != INVALID_SOCKET) + if (closesocket(hListenSocket.socket) == SOCKET_ERROR) LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); // clean up some globals (to help leak detection) @@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL); bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); -bool BindListenPort(const CService &bindAddr, std::string& strError); +bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); void StartNode(boost::thread_group& threadGroup); bool StopNode(); void SocketSendData(CNode *pnode); @@ -154,6 +154,7 @@ public: uint64_t nSendBytes; uint64_t nRecvBytes; bool fSyncNode; + bool fWhitelisted; double dPingTime; double dPingWait; std::string addrLocal; @@ -236,6 +237,7 @@ public: // store the sanitized version in cleanSubVer. The original should be used when dealing with // the network or wire types and the cleaned string used when displayed or logged. std::string strSubVer, cleanSubVer; + bool fWhitelisted; // This peer can bypass DoS banning. bool fOneShot; bool fClient; bool fInbound; @@ -259,6 +261,11 @@ protected: static std::map<CNetAddr, int64_t> setBanned; static CCriticalSection cs_setBanned; + // Whitelisted ranges. Any node connecting from these is automatically + // whitelisted (as well as those connecting to whitelisted binds). + static std::vector<CSubNet> vWhitelistedRange; + static CCriticalSection cs_vWhitelistedRange; + // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -305,6 +312,7 @@ public: addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn; nVersion = 0; strSubVer = ""; + fWhitelisted = false; fOneShot = false; fClient = false; // set by version message fInbound = fInboundIn; @@ -720,6 +728,9 @@ public: static bool Ban(const CNetAddr &ip); void copyStats(CNodeStats &stats); + static bool IsWhitelistedRange(const CNetAddr &ip); + static void AddWhitelistedRange(const CSubNet &subnet); + // Network stats static void RecordBytesRecv(uint64_t bytes); static void RecordBytesSent(uint64_t bytes); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6b3aa2a2df..5fc2f500b5 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -159,10 +159,13 @@ BitcoinGUI::BitcoinGUI(bool fIsTestnet, QWidget *parent) : labelEncryptionIcon = new QLabel(); labelConnectionsIcon = new QLabel(); labelBlocksIcon = new QLabel(); - frameBlocksLayout->addStretch(); - frameBlocksLayout->addWidget(unitDisplayControl); - frameBlocksLayout->addStretch(); - frameBlocksLayout->addWidget(labelEncryptionIcon); + if(enableWallet) + { + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(unitDisplayControl); + frameBlocksLayout->addStretch(); + frameBlocksLayout->addWidget(labelEncryptionIcon); + } frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelConnectionsIcon); frameBlocksLayout->addStretch(); @@ -780,11 +783,7 @@ void BitcoinGUI::message(const QString &title, const QString &message, unsigned if (!(buttons = (QMessageBox::StandardButton)(style & CClientUIInterface::BTN_MASK))) buttons = QMessageBox::Ok; - // Ensure we get users attention, but only if main window is visible - // as we don't want to pop up the main window for messages that happen before - // initialization is finished. - if(!(style & CClientUIInterface::NOSHOWGUI)) - showNormalIfMinimized(); + showNormalIfMinimized(); QMessageBox mBox((QMessageBox::Icon)nMBoxIcon, strTitle, message, buttons, this); int r = mBox.exec(); if (ret != NULL) @@ -921,6 +920,8 @@ void BitcoinGUI::setEncryptionStatus(int status) void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { + if(!clientModel) + return; // activateWindow() (sometimes) helps with keyboard focus on Windows if (isHidden()) { @@ -1012,7 +1013,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl():QLabel() setToolTip(tr("Unit to show amounts in. Click to select another unit.")); } -/** So that it responds to left-button clicks */ +/** So that it responds to button clicks */ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) { onDisplayUnitsClicked(event->pos()); @@ -1029,10 +1030,6 @@ void UnitDisplayStatusBarControl::createContextMenu() menu->addAction(menuAction); } connect(menu,SIGNAL(triggered(QAction*)),this,SLOT(onMenuSelection(QAction*))); - - // what happens on right click. - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(onDisplayUnitsClicked(const QPoint&))); } /** Lets the control know about the Options Model (and its signals) */ diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index e1f40ddd09..9b67f8125f 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -473,6 +473,10 @@ void RPCConsole::on_tabWidget_currentChanged(int index) { ui->lineEdit->setFocus(); } + else if(ui->tabWidget->widget(index) == ui->tab_peers) + { + initPeerTable(); + } } void RPCConsole::on_openDebugLogfileButton_clicked() @@ -648,11 +652,27 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *combinedStats) ui->peerBanScore->setText(tr("Fetching...")); } +void RPCConsole::initPeerTable() +{ + if (!clientModel) + return; + + // peerWidget needs a resize in case the dialog has non-default geometry + columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); + + // start PeerTableModel auto refresh + clientModel->getPeerTableModel()->startAutoRefresh(1000); +} + // We override the virtual resizeEvent of the QWidget to adjust tables column // sizes as the tables width is proportional to the dialogs width. void RPCConsole::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); + + if (!clientModel) + return; + columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); } @@ -660,17 +680,16 @@ void RPCConsole::showEvent(QShowEvent *event) { QWidget::showEvent(event); - // peerWidget needs a resize in case the dialog has non-default geometry - columnResizingFixer->stretchColumnWidth(PeerTableModel::Address); - - // start PeerTableModel auto refresh - clientModel->getPeerTableModel()->startAutoRefresh(1000); + initPeerTable(); } void RPCConsole::hideEvent(QHideEvent *event) { QWidget::hideEvent(event); + if (!clientModel) + return; + // stop PeerTableModel auto refresh clientModel->getPeerTableModel()->stopAutoRefresh(); } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 3aeff3eace..94672b30cc 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -47,6 +47,8 @@ protected: private: /** show detailed information on ui about selected node */ void updateNodeDetail(const CNodeCombinedStats *combinedStats); + /** initialize peer table */ + void initPeerTable(); enum ColumnWidths { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index a67f266a13..253693e624 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -276,7 +276,9 @@ Value getblock(const Array& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - ReadBlockFromDisk(block, pblockindex); + + if(!ReadBlockFromDisk(block, pblockindex)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); if (!fVerbose) { diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index c7621dc137..6f72ea7404 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -324,6 +324,7 @@ Value getblocktemplate(const Array& params, bool fHelp) ); std::string strMode = "template"; + Value lpval = Value::null; if (params.size() > 0) { const Object& oparam = params[0].get_obj(); @@ -336,6 +337,7 @@ Value getblocktemplate(const Array& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); + lpval = find_value(oparam, "longpollid"); } if (strMode != "template") @@ -347,8 +349,63 @@ Value getblocktemplate(const Array& params, bool fHelp) if (IsInitialBlockDownload()) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Bitcoin is downloading blocks..."); - // Update block static unsigned int nTransactionsUpdatedLast; + + if (lpval.type() != null_type) + { + // Wait to respond until either the best block changes, OR a minute has passed and there are more transactions + uint256 hashWatchedChain; + boost::system_time checktxtime; + unsigned int nTransactionsUpdatedLastLP; + + if (lpval.type() == str_type) + { + // Format: <hashBestChain><nTransactionsUpdatedLast> + std::string lpstr = lpval.get_str(); + + hashWatchedChain.SetHex(lpstr.substr(0, 64)); + nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); + } + else + { + // NOTE: Spec does not specify behaviour for non-string longpollid, but this makes testing easier + hashWatchedChain = chainActive.Tip()->GetBlockHash(); + nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; + } + + // Release the wallet and main lock while waiting +#ifdef ENABLE_WALLET + if(pwalletMain) + LEAVE_CRITICAL_SECTION(pwalletMain->cs_wallet); +#endif + LEAVE_CRITICAL_SECTION(cs_main); + { + checktxtime = boost::get_system_time() + boost::posix_time::minutes(1); + + boost::unique_lock<boost::mutex> lock(csBestBlock); + while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) + { + if (!cvBlockChange.timed_wait(lock, checktxtime)) + { + // Timeout: Check transactions for update + if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) + break; + checktxtime += boost::posix_time::seconds(10); + } + } + } + ENTER_CRITICAL_SECTION(cs_main); +#ifdef ENABLE_WALLET + if(pwalletMain) + ENTER_CRITICAL_SECTION(pwalletMain->cs_wallet); +#endif + + if (!IsRPCRunning()) + throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); + // TODO: Maybe recheck connections/IBD and (if something wrong) send an expires-immediately template to stop miners? + } + + // Update block static CBlockIndex* pindexPrev; static int64_t nStart; static CBlockTemplate* pblocktemplate; @@ -436,6 +493,7 @@ Value getblocktemplate(const Array& params, bool fHelp) result.push_back(Pair("transactions", transactions)); result.push_back(Pair("coinbaseaux", aux)); result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue)); + result.push_back(Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() + i64tostr(nTransactionsUpdatedLast))); result.push_back(Pair("target", hashTarget.GetHex())); result.push_back(Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast()+1)); result.push_back(Pair("mutable", aMutable)); diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index cf2c293caf..680717930b 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.cpp @@ -137,6 +137,7 @@ Value getpeerinfo(const Array& params, bool fHelp) obj.push_back(Pair("syncheight", statestats.nSyncHeight)); } obj.push_back(Pair("syncnode", stats.fSyncNode)); + obj.push_back(Pair("whitelisted", stats.fWhitelisted)); ret.push_back(obj); } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 2306b1b883..1efe38e830 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -523,7 +523,7 @@ Value signrawtransaction(const Array& params, bool fHelp) " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\" (string, required) redeem script\n" + " \"redeemScript\": \"hex\" (string, required for P2SH) redeem script\n" " }\n" " ,...\n" " ]\n" diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index e0c96d88fa..5deb6a4e08 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -32,6 +32,7 @@ using namespace std; static std::string strRPCUserColonPass; +static bool fRPCRunning = false; // These are created by StartRPCThreads, destroyed in StopRPCThreads static asio::io_service* rpc_io_service = NULL; static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; @@ -232,8 +233,8 @@ static const CRPCCommand vRPCCommands[] = { "getblockchaininfo", &getblockchaininfo, true, false, false }, { "getbestblockhash", &getbestblockhash, true, false, false }, { "getblockcount", &getblockcount, true, false, false }, - { "getblock", &getblock, false, false, false }, - { "getblockhash", &getblockhash, false, false, false }, + { "getblock", &getblock, true, false, false }, + { "getblockhash", &getblockhash, true, false, false }, { "getdifficulty", &getdifficulty, true, false, false }, { "getrawmempool", &getrawmempool, true, false, false }, { "gettxout", &gettxout, true, false, false }, @@ -245,32 +246,32 @@ static const CRPCCommand vRPCCommands[] = { "getmininginfo", &getmininginfo, true, false, false }, { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "prioritisetransaction", &prioritisetransaction, true, false, false }, - { "submitblock", &submitblock, false, true, false }, + { "submitblock", &submitblock, true, true, false }, /* Raw transactions */ - { "createrawtransaction", &createrawtransaction, false, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false, false }, - { "decodescript", &decodescript, false, false, false }, - { "getrawtransaction", &getrawtransaction, false, false, false }, + { "createrawtransaction", &createrawtransaction, true, false, false }, + { "decoderawtransaction", &decoderawtransaction, true, false, false }, + { "decodescript", &decodescript, true, false, false }, + { "getrawtransaction", &getrawtransaction, true, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false, false }, { "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ /* Utility functions */ { "createmultisig", &createmultisig, true, true , false }, { "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ - { "verifymessage", &verifymessage, false, false, false }, + { "verifymessage", &verifymessage, true, false, false }, { "estimatefee", &estimatefee, true, true, false }, { "estimatepriority", &estimatepriority, true, true, false }, #ifdef ENABLE_WALLET /* Wallet */ - { "addmultisigaddress", &addmultisigaddress, false, false, true }, + { "addmultisigaddress", &addmultisigaddress, true, false, true }, { "backupwallet", &backupwallet, true, false, true }, { "dumpprivkey", &dumpprivkey, true, false, true }, { "dumpwallet", &dumpwallet, true, false, true }, - { "encryptwallet", &encryptwallet, false, false, true }, + { "encryptwallet", &encryptwallet, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, - { "getaccount", &getaccount, false, false, true }, + { "getaccount", &getaccount, true, false, true }, { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, { "getbalance", &getbalance, false, false, true }, { "getnewaddress", &getnewaddress, true, false, true }, @@ -279,10 +280,10 @@ static const CRPCCommand vRPCCommands[] = { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, { "gettransaction", &gettransaction, false, false, true }, { "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, - { "getwalletinfo", &getwalletinfo, true, false, true }, - { "importprivkey", &importprivkey, false, false, true }, - { "importwallet", &importwallet, false, false, true }, - { "importaddress", &importaddress, false, false, true }, + { "getwalletinfo", &getwalletinfo, false, false, true }, + { "importprivkey", &importprivkey, true, false, true }, + { "importwallet", &importwallet, true, false, true }, + { "importaddress", &importaddress, true, false, true }, { "keypoolrefill", &keypoolrefill, true, false, true }, { "listaccounts", &listaccounts, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, @@ -292,16 +293,16 @@ static const CRPCCommand vRPCCommands[] = { "listsinceblock", &listsinceblock, false, false, true }, { "listtransactions", &listtransactions, false, false, true }, { "listunspent", &listunspent, false, false, true }, - { "lockunspent", &lockunspent, false, false, true }, + { "lockunspent", &lockunspent, true, false, true }, { "move", &movecmd, false, false, true }, { "sendfrom", &sendfrom, false, false, true }, { "sendmany", &sendmany, false, false, true }, { "sendtoaddress", &sendtoaddress, false, false, true }, { "setaccount", &setaccount, true, false, true }, - { "settxfee", &settxfee, false, false, true }, - { "signmessage", &signmessage, false, false, true }, + { "settxfee", &settxfee, true, false, true }, + { "signmessage", &signmessage, true, false, true }, { "walletlock", &walletlock, true, false, true }, - { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, + { "walletpassphrasechange", &walletpassphrasechange, true, false, true }, { "walletpassphrase", &walletpassphrase, true, false, true }, /* Wallet-enabled mining */ @@ -659,6 +660,7 @@ void StartRPCThreads() rpc_worker_group = new boost::thread_group(); for (int i = 0; i < GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; } void StartDummyRPCThread() @@ -671,12 +673,15 @@ void StartDummyRPCThread() rpc_dummy_work = new asio::io_service::work(*rpc_io_service); rpc_worker_group = new boost::thread_group(); rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); + fRPCRunning = true; } } void StopRPCThreads() { if (rpc_io_service == NULL) return; + // Set this to false first, so that longpolling loops will exit when woken up + fRPCRunning = false; // First, cancel all timers and acceptors // This is not done automatically by ->stop(), and in some cases the destructor of @@ -698,6 +703,7 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); + cvBlockChange.notify_all(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); delete rpc_dummy_work; rpc_dummy_work = NULL; @@ -706,6 +712,11 @@ void StopRPCThreads() delete rpc_io_service; rpc_io_service = NULL; } +bool IsRPCRunning() +{ + return fRPCRunning; +} + void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func) { if (!err) diff --git a/src/rpcserver.h b/src/rpcserver.h index e32eb975a1..31badadd6d 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -40,6 +40,8 @@ void StartRPCThreads(); void StartDummyRPCThread(); /* Stop RPC threads */ void StopRPCThreads(); +/* Query whether RPC is running */ +bool IsRPCRunning(); /* Type-check arguments; throws JSONRPCError if wrong type given. Does not check that diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 1e46129065..e8c62fd37b 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -201,7 +201,7 @@ Value getrawchangeaddress(const Array& params, bool fHelp) CReserveKey reservekey(pwalletMain); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey)) - throw JSONRPCError(RPC_WALLET_ERROR, "Error: Unable to obtain key for change"); + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); reservekey.KeepKey(); diff --git a/src/sync.h b/src/sync.h index 077ed59b89..cd319e0171 100644 --- a/src/sync.h +++ b/src/sync.h @@ -84,6 +84,9 @@ typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection; /** Wrapped boost mutex: supports waiting but not recursive locking */ typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; +/** Just a typedef for boost::condition_variable, can be wrapped later if desired */ +typedef boost::condition_variable CConditionVariable; + #ifdef DEBUG_LOCKORDER void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); void LeaveCritical(); diff --git a/src/ui_interface.h b/src/ui_interface.h index e9fcd91d41..a48ba237d2 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -63,8 +63,6 @@ public: /** Force blocking, modal message box dialog (not just OS notification) */ MODAL = 0x10000000U, - /** Don't bring GUI to foreground. Use for messages during initialization */ - NOSHOWGUI = 0x20000000U, /** Predefined combinations for certain default usage cases */ MSG_INFORMATION = ICON_INFORMATION, diff --git a/src/wallet.cpp b/src/wallet.cpp index d61f01d096..8d27da9d40 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1076,7 +1076,7 @@ int64_t CWallet::GetWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -1092,7 +1092,7 @@ int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -1107,7 +1107,7 @@ int64_t CWallet::GetImmatureWatchOnlyBalance() const { int64_t nTotal = 0; { - LOCK(cs_wallet); + LOCK2(cs_main, cs_wallet); for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; @@ -2011,11 +2011,7 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey) if (nIndex != -1) vchPubKey = keypool.vchPubKey; else { - if (pwallet->vchDefaultKey.IsValid()) { - LogPrintf("CReserveKey::GetReservedKey(): Warning: Using default key instead of a new key, top up your keypool!"); - vchPubKey = pwallet->vchDefaultKey; - } else - return false; + return false; } } assert(vchPubKey.IsValid()); |