From a9d771e49729ba89f7047963ce3d2fd4d0750211 Mon Sep 17 00:00:00 2001 From: Oleg Girko Date: Fri, 21 Jul 2017 10:35:19 +0100 Subject: [PATCH] Backport Bitcoin PR#8085: p2p: Begin encapsulation (#1537) * net: move CBanDB and CAddrDB out of net.h/cpp This will eventually solve a circular dependency * net: Create CConnman to encapsulate p2p connections * net: Move socket binding into CConnman * net: move OpenNetworkConnection into CConnman * net: move ban and addrman functions into CConnman * net: Add oneshot functions to CConnman * net: move added node functions to CConnman * net: Add most functions needed for vNodes to CConnman * net: handle nodesignals in CConnman * net: Pass CConnection to wallet rather than using the global * net: Add rpc error for missing/disabled p2p functionality * net: Pass CConnman around as needed * gui: add NodeID to the peer table * net: create generic functor accessors and move vNodes to CConnman * net: move whitelist functions into CConnman * net: move nLastNodeId to CConnman * net: move nLocalHostNonce to CConnman This behavior seems to have been quite racy and broken. Move nLocalHostNonce into CNode, and check received nonces against all non-fully-connected nodes. If there's a match, assume we've connected to ourself. * net: move messageHandlerCondition to CConnman * net: move send/recv statistics to CConnman * net: move SendBufferSize/ReceiveFloodSize to CConnman * net: move nLocalServices/nRelevantServices to CConnman These are in-turn passed to CNode at connection time. This allows us to offer different services to different peers (or test the effects of doing so). * net: move semOutbound and semMasternodeOutbound to CConnman * net: SocketSendData returns written size * net: move max/max-outbound to CConnman * net: Pass best block known height into CConnman CConnman then passes the current best height into CNode at creation time. This way CConnman/CNode have no dependency on main for height, and the signals only move in one direction. This also helps to prevent identity leakage a tiny bit. Before this change, an attacker could theoretically make 2 connections on different interfaces. They would connect fully on one, and only establish the initial connection on the other. Once they receive a new block, they would relay it to your first connection, and immediately commence the version handshake on the second. Since the new block height is reflected immediately, they could attempt to learn whether the two connections were correlated. This is, of course, incredibly unlikely to work due to the small timings involved and receipt from other senders. But it doesn't hurt to lock-in nBestHeight at the time of connection, rather than letting the remote choose the time. * net: pass CClientUIInterface into CConnman * net: Drop StartNode/StopNode and use CConnman directly * net: Introduce CConnection::Options to avoid passing so many params * net: add nSendBufferMaxSize/nReceiveFloodSize to CConnection::Options * net: move vNodesDisconnected into CConnman * Made the ForEachNode* functions in src/net.cpp more pragmatic and self documenting * Convert ForEachNode* functions to take a templated function argument rather than a std::function to eliminate std::function overhead * net: move MAX_FEELER_CONNECTIONS into connman --- src/Makefile.am | 2 + src/activemasternode.cpp | 41 +- src/addrdb.cpp | 218 ++++++++++ src/addrdb.h | 103 +++++ src/darksend-relay.cpp | 3 +- src/governance-object.cpp | 2 +- src/governance-vote.cpp | 2 +- src/governance.cpp | 4 +- src/init.cpp | 64 ++- src/instantx.cpp | 4 +- src/main.cpp | 157 +++---- src/main.h | 10 +- src/masternode-payments.cpp | 2 +- src/masternode-sync.cpp | 59 +-- src/masternode.cpp | 4 +- src/masternode.h | 2 +- src/masternodeman.cpp | 12 +- src/miner.cpp | 20 +- src/miner.h | 3 +- src/net.cpp | 845 +++++++++++++++++------------------- src/net.h | 568 ++++++++++++++---------- src/privatesend-client.cpp | 84 ++-- src/privatesend-client.h | 13 +- src/privatesend-server.cpp | 18 +- src/privatesend.cpp | 4 +- src/qt/bantablemodel.cpp | 3 +- src/qt/clientmodel.cpp | 26 +- src/qt/guiutil.cpp | 8 +- src/qt/guiutil.h | 2 +- src/qt/overviewpage.cpp | 2 +- src/qt/peertablemodel.cpp | 21 +- src/qt/peertablemodel.h | 7 +- src/qt/rpcconsole.cpp | 35 +- src/qt/walletmodel.cpp | 2 +- src/rpc/blockchain.cpp | 4 +- src/rpc/governance.cpp | 2 +- src/rpc/masternode.cpp | 5 +- src/rpc/mining.cpp | 11 +- src/rpc/misc.cpp | 11 +- src/rpc/net.cpp | 108 +++-- src/rpc/protocol.h | 1 + src/rpc/rawtransaction.cpp | 8 +- src/sendalert.cpp | 10 +- src/spork.cpp | 2 +- src/test/DoS_tests.cpp | 56 +-- src/test/miner_tests.cpp | 2 +- src/test/net_tests.cpp | 6 +- src/test/test_dash.cpp | 7 +- src/test/test_dash.h | 2 + src/validationinterface.cpp | 4 +- src/validationinterface.h | 5 +- src/wallet/rpcwallet.cpp | 15 +- src/wallet/wallet.cpp | 20 +- src/wallet/wallet.h | 8 +- 54 files changed, 1567 insertions(+), 1070 deletions(-) create mode 100644 src/addrdb.cpp create mode 100644 src/addrdb.h diff --git a/src/Makefile.am b/src/Makefile.am index 37a4ffa06..65fc7662b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,7 @@ endif .PHONY: FORCE check-symbols check-security # dash core # BITCOIN_CORE_H = \ + addrdb.h \ activemasternode.h \ addressindex.h \ spentindex.h \ @@ -207,6 +208,7 @@ libbitcoin_server_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CP libbitcoin_server_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_server_a_SOURCES = \ addrman.cpp \ + addrdb.cpp \ alert.cpp \ bloom.cpp \ chain.cpp \ diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index ff51ce066..1d953f26f 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -151,27 +151,23 @@ void CActiveMasternode::ManageStateInitial() return; } - bool fFoundLocal = false; - { - LOCK(cs_vNodes); - - // First try to find whatever local address is specified by externalip option - fFoundLocal = GetLocal(service) && CMasternode::IsValidNetAddr(service); - if(!fFoundLocal) { - // nothing and no live connections, can't do anything for now - if (vNodes.empty()) { - nState = ACTIVE_MASTERNODE_NOT_CAPABLE; - strNotCapableReason = "Can't detect valid external address. Will retry when there are some connections available."; - LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); - return; - } - // We have some peers, let's try to find our local address from one of them - BOOST_FOREACH(CNode* pnode, vNodes) { - if (pnode->fSuccessfullyConnected && pnode->addr.IsIPv4()) { - fFoundLocal = GetLocal(service, &pnode->addr) && CMasternode::IsValidNetAddr(service); - if(fFoundLocal) break; - } - } + // First try to find whatever local address is specified by externalip option + bool fFoundLocal = GetLocal(service) && CMasternode::IsValidNetAddr(service); + if(!fFoundLocal) { + bool empty = true; + // If we have some peers, let's try to find our local address from one of them + g_connman->ForEachNodeContinueIf([&fFoundLocal, &empty, this](CNode* pnode) { + empty = false; + if (pnode->fSuccessfullyConnected && pnode->addr.IsIPv4()) + fFoundLocal = GetLocal(service, &pnode->addr) && CMasternode::IsValidNetAddr(service); + return !fFoundLocal; + }); + // nothing and no live connections, can't do anything for now + if (empty) { + nState = ACTIVE_MASTERNODE_NOT_CAPABLE; + strNotCapableReason = "Can't detect valid external address. Will retry when there are some connections available."; + LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); + return; } } @@ -199,7 +195,8 @@ void CActiveMasternode::ManageStateInitial() LogPrintf("CActiveMasternode::ManageStateInitial -- Checking inbound connection to '%s'\n", service.ToString()); - if(!ConnectNode(CAddress(service, NODE_NETWORK), NULL, true)) { + // TODO: Pass CConnman instance somehow and don't use global variable. + if(!g_connman->ConnectNode(CAddress(service, NODE_NETWORK), NULL, true)) { nState = ACTIVE_MASTERNODE_NOT_CAPABLE; strNotCapableReason = "Could not connect to " + service.ToString(); LogPrintf("CActiveMasternode::ManageStateInitial -- %s: %s\n", GetStateString(), strNotCapableReason); diff --git a/src/addrdb.cpp b/src/addrdb.cpp new file mode 100644 index 000000000..ddf41f92d --- /dev/null +++ b/src/addrdb.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "addrdb.h" + +#include "addrman.h" +#include "chainparams.h" +#include "clientversion.h" +#include "hash.h" +#include "random.h" +#include "streams.h" +#include "tinyformat.h" +#include "util.h" + +#include + +CBanDB::CBanDB() +{ + pathBanlist = GetDataDir() / "banlist.dat"; +} + +bool CBanDB::Write(const banmap_t& banSet) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("banlist.dat.%04x", randv); + + // serialize banlist, checksum data up to that point, then append csum + CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); + ssBanlist << FLATDATA(Params().MessageStart()); + ssBanlist << banSet; + uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); + ssBanlist << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssBanlist; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing banlist.dat, if any, with new banlist.dat.XXXX + if (!RenameOver(pathTmp, pathBanlist)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CBanDB::Read(banmap_t& banSet) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathBanlist.string().c_str(), "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathBanlist.string()); + + // use file size to size memory buffer + uint64_t fileSize = boost::filesystem::file_size(pathBanlist); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + std::vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); + if (hashIn != hashTmp) + return error("%s: Checksum mismatch, data corrupted", __func__); + + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssBanlist >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s: Invalid network magic number", __func__); + + // de-serialize address data into one CAddrMan object + ssBanlist >> banSet; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} + +CAddrDB::CAddrDB() +{ + pathAddr = GetDataDir() / "peers.dat"; +} + +bool CAddrDB::Write(const CAddrMan& addr) +{ + // Generate random temporary filename + unsigned short randv = 0; + GetRandBytes((unsigned char*)&randv, sizeof(randv)); + std::string tmpfn = strprintf("peers.dat.%04x", randv); + + // serialize addresses, checksum data up to that point, then append csum + CDataStream ssPeers(SER_DISK, CLIENT_VERSION); + ssPeers << FLATDATA(Params().MessageStart()); + ssPeers << addr; + uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); + ssPeers << hash; + + // open temp output file, and associate with CAutoFile + boost::filesystem::path pathTmp = GetDataDir() / tmpfn; + FILE *file = fopen(pathTmp.string().c_str(), "wb"); + CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); + if (fileout.IsNull()) + return error("%s: Failed to open file %s", __func__, pathTmp.string()); + + // Write and commit header, data + try { + fileout << ssPeers; + } + catch (const std::exception& e) { + return error("%s: Serialize or I/O error - %s", __func__, e.what()); + } + FileCommit(fileout.Get()); + fileout.fclose(); + + // replace existing peers.dat, if any, with new peers.dat.XXXX + if (!RenameOver(pathTmp, pathAddr)) + return error("%s: Rename-into-place failed", __func__); + + return true; +} + +bool CAddrDB::Read(CAddrMan& addr) +{ + // open input file, and associate with CAutoFile + FILE *file = fopen(pathAddr.string().c_str(), "rb"); + CAutoFile filein(file, SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) + return error("%s: Failed to open file %s", __func__, pathAddr.string()); + + // use file size to size memory buffer + uint64_t fileSize = boost::filesystem::file_size(pathAddr); + uint64_t dataSize = 0; + // Don't try to resize to a negative number if file is small + if (fileSize >= sizeof(uint256)) + dataSize = fileSize - sizeof(uint256); + std::vector vchData; + vchData.resize(dataSize); + uint256 hashIn; + + // read data and checksum from file + try { + filein.read((char *)&vchData[0], dataSize); + filein >> hashIn; + } + catch (const std::exception& e) { + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + filein.fclose(); + + CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); + + // verify stored checksum matches input data + uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); + if (hashIn != hashTmp) + return error("%s: Checksum mismatch, data corrupted", __func__); + + return Read(addr, ssPeers); +} + +bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) +{ + unsigned char pchMsgTmp[4]; + try { + // de-serialize file header (network specific magic number) and .. + ssPeers >> FLATDATA(pchMsgTmp); + + // ... verify the network matches ours + if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) + return error("%s: Invalid network magic number", __func__); + + // de-serialize address data into one CAddrMan object + ssPeers >> addr; + } + catch (const std::exception& e) { + // de-serialization has failed, ensure addrman is left in a clean state + addr.Clear(); + return error("%s: Deserialize or I/O error - %s", __func__, e.what()); + } + + return true; +} diff --git a/src/addrdb.h b/src/addrdb.h new file mode 100644 index 000000000..d8c66d872 --- /dev/null +++ b/src/addrdb.h @@ -0,0 +1,103 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_ADDRDB_H +#define BITCOIN_ADDRDB_H + +#include "serialize.h" + +#include +#include +#include + +class CSubNet; +class CAddrMan; +class CDataStream; + +typedef enum BanReason +{ + BanReasonUnknown = 0, + BanReasonNodeMisbehaving = 1, + BanReasonManuallyAdded = 2 +} BanReason; + +class CBanEntry +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; + int64_t nBanUntil; + uint8_t banReason; + + CBanEntry() + { + SetNull(); + } + + CBanEntry(int64_t nCreateTimeIn) + { + SetNull(); + nCreateTime = nCreateTimeIn; + } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(this->nVersion); + nVersion = this->nVersion; + READWRITE(nCreateTime); + READWRITE(nBanUntil); + READWRITE(banReason); + } + + void SetNull() + { + nVersion = CBanEntry::CURRENT_VERSION; + nCreateTime = 0; + nBanUntil = 0; + banReason = BanReasonUnknown; + } + + std::string banReasonToString() + { + switch (banReason) { + case BanReasonNodeMisbehaving: + return "node misbehaving"; + case BanReasonManuallyAdded: + return "manually added"; + default: + return "unknown"; + } + } +}; + +typedef std::map banmap_t; + +/** Access to the (IP) address database (peers.dat) */ +class CAddrDB +{ +private: + boost::filesystem::path pathAddr; +public: + CAddrDB(); + bool Write(const CAddrMan& addr); + bool Read(CAddrMan& addr); + bool Read(CAddrMan& addr, CDataStream& ssPeers); +}; + +/** Access to the banlist database (banlist.dat) */ +class CBanDB +{ +private: + boost::filesystem::path pathBanlist; +public: + CBanDB(); + bool Write(const banmap_t& banSet); + bool Read(banmap_t& banSet); +}; + +#endif // BITCOIN_ADDRDB_H diff --git a/src/darksend-relay.cpp b/src/darksend-relay.cpp index 3847ca743..07d258062 100644 --- a/src/darksend-relay.cpp +++ b/src/darksend-relay.cpp @@ -103,7 +103,8 @@ void CDarkSendRelay::RelayThroughNode(int nRank) if(pmn != NULL){ //printf("RelayThroughNode %s\n", pmn->addr.ToString().c_str()); - CNode* pnode = ConnectNode((CAddress)pmn->addr, NULL); + // TODO: Pass CConnman instance somehow and don't use global variable. + CNode* pnode = g_connman->ConnectNode((CAddress)pmn->addr, NULL); if(pnode) { //printf("Connected\n"); pnode->PushMessage("dsr", (*this)); diff --git a/src/governance-object.cpp b/src/governance-object.cpp index 3331e3a2e..3665857be 100644 --- a/src/governance-object.cpp +++ b/src/governance-object.cpp @@ -678,7 +678,7 @@ bool CGovernanceObject::GetCurrentMNVotes(const CTxIn& mnCollateralOutpoint, vot void CGovernanceObject::Relay() { CInv inv(MSG_GOVERNANCE_OBJECT, GetHash()); - RelayInv(inv, PROTOCOL_VERSION); + g_connman->RelayInv(inv, PROTOCOL_VERSION); } void CGovernanceObject::UpdateSentinelVariables() diff --git a/src/governance-vote.cpp b/src/governance-vote.cpp index d6fab26cb..a3f15bd23 100644 --- a/src/governance-vote.cpp +++ b/src/governance-vote.cpp @@ -226,7 +226,7 @@ CGovernanceVote::CGovernanceVote(CTxIn vinMasternodeIn, uint256 nParentHashIn, v void CGovernanceVote::Relay() const { CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash()); - RelayInv(inv, PROTOCOL_VERSION); + g_connman->RelayInv(inv, PROTOCOL_VERSION); } bool CGovernanceVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode) diff --git a/src/governance.cpp b/src/governance.cpp index aa6dbd6ca..c6b223d62 100644 --- a/src/governance.cpp +++ b/src/governance.cpp @@ -1421,7 +1421,7 @@ void CGovernanceManager::UpdatedBlockTip(const CBlockIndex *pindex) void CGovernanceManager::RequestOrphanObjects() { - std::vector vNodesCopy = CopyNodeVector(); + std::vector vNodesCopy = g_connman->CopyNodeVector(); std::vector vecHashesFiltered; { @@ -1448,7 +1448,7 @@ void CGovernanceManager::RequestOrphanObjects() } } - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); } void CGovernanceManager::CleanOrphanObjects() diff --git a/src/init.cpp b/src/init.cpp index 9ab371263..293152a26 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -63,6 +63,7 @@ #include #include +#include #ifndef WIN32 #include @@ -85,7 +86,7 @@ using namespace std; -extern void ThreadSendAlert(); +extern void ThreadSendAlert(CConnman& connman); #ifdef ENABLE_WALLET CWallet* pwalletMain = NULL; @@ -97,6 +98,8 @@ static const bool DEFAULT_REST_ENABLE = false; static const bool DEFAULT_DISABLE_SAFEMODE = false; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; +std::unique_ptr g_connman; + #if ENABLE_ZMQ static CZMQNotificationInterface* pzmqNotificationInterface = NULL; #endif @@ -223,8 +226,10 @@ void PrepareShutdown() if (pwalletMain) pwalletMain->Flush(false); #endif - GenerateBitcoins(false, 0, Params()); - StopNode(); + GenerateBitcoins(false, 0, Params(), *g_connman); + MapPort(false); + g_connman->Stop(); + g_connman.reset(); // STORE DATA CACHES INTO SERIALIZED DAT FILES CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); @@ -343,11 +348,11 @@ bool static InitWarning(const std::string &str) return true; } -bool static Bind(const CService &addr, unsigned int flags) { +bool static Bind(CConnman& connman, 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 (!connman.BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) { if (flags & BF_REPORT_ERROR) return InitError(strError); return false; @@ -1004,7 +1009,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Make sure enough file descriptors are available int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1); int nUserMaxConnections = GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS); - nMaxConnections = std::max(nUserMaxConnections, 0); + int nMaxConnections = std::max(nUserMaxConnections, 0); // Trim requested connection counts, to fit into system limitations nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0); @@ -1169,6 +1174,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // Option to startup with mocktime set (used for regression testing): SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op + ServiceFlags nLocalServices = NODE_NETWORK; + ServiceFlags nRelevantServices = NODE_NETWORK; + if (GetBoolArg("-peerbloomfilters", true)) nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); @@ -1305,6 +1313,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) #endif // ENABLE_WALLET // ********************************************************* Step 6: network initialization + assert(!g_connman); + g_connman = std::unique_ptr(new CConnman()); + CConnman& connman = *g_connman; + RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size @@ -1341,7 +1353,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) CSubNet subnet(net); if (!subnet.IsValid()) return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net)); - CNode::AddWhitelistedRange(subnet); + connman.AddWhitelistedRange(subnet); } } @@ -1390,7 +1402,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) 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)); + fBound |= Bind(connman, addrBind, (BF_EXPLICIT | BF_REPORT_ERROR)); } BOOST_FOREACH(const std::string& strBind, mapMultiArgs["-whitebind"]) { CService addrBind; @@ -1398,14 +1410,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) 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)); + fBound |= Bind(connman, 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); + fBound |= Bind(connman, CService(in6addr_any, GetListenPort()), BF_NONE); + fBound |= Bind(connman, 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.")); @@ -1421,7 +1433,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } BOOST_FOREACH(const std::string& strDest, mapMultiArgs["-seednode"]) - AddOneShot(strDest); + connman.AddOneShot(strDest); #if ENABLE_ZMQ pzmqNotificationInterface = CZMQNotificationInterface::CreateWithArguments(mapArgs); @@ -1435,7 +1447,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) RegisterValidationInterface(pdsNotificationInterface); if (mapArgs.count("-maxuploadtarget")) { - CNode::SetMaxOutboundTarget(GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024); + connman.SetMaxOutboundTarget(GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024); } // ********************************************************* Step 7: load block chain @@ -1956,7 +1968,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (fMasterNode) threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendServer)); else - threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendClient)); + threadGroup.create_thread(boost::bind(&ThreadCheckPrivateSendClient, boost::ref(*g_connman))); // ********************************************************* Step 12: start node @@ -1986,10 +1998,28 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); - StartNode(threadGroup, scheduler); + Discover(threadGroup); + + // Map ports with UPnP + MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); + + std::string strNodeError; + CConnman::Options connOptions; + connOptions.nLocalServices = nLocalServices; + connOptions.nRelevantServices = nRelevantServices; + connOptions.nMaxConnections = nMaxConnections; + connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); + connOptions.nMaxFeeler = 1; + connOptions.nBestHeight = chainActive.Height(); + connOptions.uiInterface = &uiInterface; + connOptions.nSendBufferMaxSize = 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); + connOptions.nReceiveFloodSize = 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + + if(!connman.Start(threadGroup, scheduler, strNodeError, connOptions)) + return InitError(strNodeError); // Generate coins in the background - GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams); + GenerateBitcoins(GetBoolArg("-gen", DEFAULT_GENERATE), GetArg("-genproclimit", DEFAULT_GENERATE_THREADS), chainparams, connman); // ********************************************************* Step 13: finished @@ -2006,7 +2036,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } #endif - threadGroup.create_thread(boost::bind(&ThreadSendAlert)); + threadGroup.create_thread(boost::bind(&ThreadSendAlert, boost::ref(connman))); return !fRequestShutdown; } diff --git a/src/instantx.cpp b/src/instantx.cpp index f8b1ad216..14a9abdb4 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -1067,7 +1067,7 @@ bool CTxLockVote::Sign() void CTxLockVote::Relay() const { CInv inv(MSG_TXLOCK_VOTE, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } bool CTxLockVote::IsExpired(int nHeight) const @@ -1168,7 +1168,7 @@ bool CTxLockCandidate::IsExpired(int nHeight) const void CTxLockCandidate::Relay() const { - RelayTransaction(txLockRequest); + g_connman->RelayTransaction(txLockRequest); std::map::const_iterator itOutpointLock = mapOutPointLocks.begin(); while(itOutpointLock != mapOutPointLocks.end()) { itOutpointLock->second.Relay(); diff --git a/src/main.cpp b/src/main.cpp index 4588432b8..18a21eb47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -308,12 +308,6 @@ CNodeState *State(NodeId pnode) { return &it->second; } -int GetHeight() -{ - LOCK(cs_main); - return chainActive.Height(); -} - void UpdatePreferredDownload(CNode* node, CNodeState* state) { nPreferredDownload -= state->fPreferredDownload; @@ -331,7 +325,8 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) { state.address = pnode->addr; } -void FinalizeNode(NodeId nodeid) { +void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { + fUpdateConnectionTime = false; LOCK(cs_main); CNodeState *state = State(nodeid); @@ -339,7 +334,7 @@ void FinalizeNode(NodeId nodeid) { nSyncStarted--; if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - AddressCurrentlyConnected(state->address); + fUpdateConnectionTime = true; } BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight) { @@ -577,7 +572,6 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { void RegisterNodeSignals(CNodeSignals& nodeSignals) { - nodeSignals.GetHeight.connect(&GetHeight); nodeSignals.ProcessMessages.connect(&ProcessMessages); nodeSignals.SendMessages.connect(&SendMessages); nodeSignals.InitializeNode.connect(&InitializeNode); @@ -586,7 +580,6 @@ void RegisterNodeSignals(CNodeSignals& nodeSignals) void UnregisterNodeSignals(CNodeSignals& nodeSignals) { - nodeSignals.GetHeight.disconnect(&GetHeight); nodeSignals.ProcessMessages.disconnect(&ProcessMessages); nodeSignals.SendMessages.disconnect(&SendMessages); nodeSignals.InitializeNode.disconnect(&InitializeNode); @@ -3317,7 +3310,7 @@ static void NotifyHeaderTip() { * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock) { +bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, const CBlock *pblock, CConnman* connman) { CBlockIndex *pindexMostWork = NULL; CBlockIndex *pindexNewTip = NULL; do { @@ -3355,6 +3348,9 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, // When we reach this point, we switched to a new tip (stored in pindexNewTip). // Notifications/callbacks that can run without cs_main + if(connman) + connman->SetBestHeight(chainActive.Height()); + // Always notify the UI if a new block tip was connected if (pindexFork != pindexNewTip) { uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); @@ -3376,15 +3372,14 @@ bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, int nBlockEstimate = 0; if (fCheckpointsEnabled) nBlockEstimate = Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()); - { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + if(connman) { + connman->ForEachNode([nNewHeight, nBlockEstimate, &vHashes](CNode* pnode) { if (nNewHeight > (pnode->nStartingHeight != -1 ? pnode->nStartingHeight - 2000 : nBlockEstimate)) { BOOST_REVERSE_FOREACH(const uint256& hash, vHashes) { pnode->PushBlockHash(hash); } } - } + }); } // Notify external listeners about the new tip. if (!vHashes.empty()) { @@ -3982,7 +3977,7 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned } -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp) +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman) { { LOCK(cs_main); @@ -4004,7 +3999,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, C NotifyHeaderTip(); - if (!ActivateBestChain(state, chainparams, pblock)) + if (!ActivateBestChain(state, chainparams, pblock, connman)) return error("%s: ActivateBestChain failed", __func__); masternodeSync.IsBlockchainSynced(true); @@ -4969,9 +4964,43 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return true; } -void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams) +static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman) +{ + int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) + + // Relay to a limited number of other nodes + // Use deterministic randomness to send to the same nodes for 24 hours + // at a time so the addrKnowns of the chosen nodes prevent repeats + static uint256 hashSalt; + if (hashSalt.IsNull()) + hashSalt = GetRandHash(); + uint64_t hashAddr = addr.GetHash(); + uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); + hashRand = Hash(BEGIN(hashRand), END(hashRand)); + std::multimap mapMix; + + auto sortfunc = [&mapMix, &hashRand](CNode* pnode) { + if (pnode->nVersion >= CADDR_TIME_VERSION) { + unsigned int nPointer; + memcpy(&nPointer, &pnode, sizeof(nPointer)); + uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); + hashKey = Hash(BEGIN(hashKey), END(hashKey)); + mapMix.emplace(hashKey, pnode); + } + }; + + auto pushfunc = [&addr, &mapMix, &nRelayNodes] { + for (auto mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) + mi->second->PushAddress(addr); + }; + + connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); +} + +void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman) { std::deque::iterator it = pfrom->vRecvGetData.begin(); + unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); vector vNotFound; @@ -4979,7 +5008,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam while (it != pfrom->vRecvGetData.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= SendBufferSize()) + if (pfrom->nSendSize >= nMaxSendBufferSize) break; const CInv &inv = *it; @@ -5012,7 +5041,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && CNode::OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != NULL) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint("net", "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -5257,10 +5286,12 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } -bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived) +bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived, CConnman& connman) { const CChainParams& chainparams = Params(); RandAddSeedPerfmon(); + unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); + LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) @@ -5269,7 +5300,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } - if (!(nLocalServices & NODE_BLOOM) && + if (!(pfrom->GetLocalServices() & NODE_BLOOM) && (strCommand == NetMsgType::FILTERLOAD || strCommand == NetMsgType::FILTERADD || strCommand == NetMsgType::FILTERCLEAR)) @@ -5311,7 +5342,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { - addrman.SetServices(pfrom->addr, pfrom->nServices); + connman.SetServices(pfrom->addr, pfrom->nServices); } if (pfrom->nServicesExpected & ~pfrom->nServices) { @@ -5348,7 +5379,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->fRelayTxes = true; // Disconnect if we connected to ourself - if (nNonce == nLocalHostNonce && nNonce > 1) + if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; @@ -5382,7 +5413,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Advertise our address if (fListen && !IsInitialBlockDownload()) { - CAddress addr = GetLocalAddress(&pfrom->addr); + CAddress addr = GetLocalAddress(&pfrom->addr, pfrom->GetLocalServices()); if (addr.IsRoutable()) { LogPrintf("ProcessMessages: advertising address %s\n", addr.ToString()); @@ -5395,17 +5426,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // Get recent addresses - if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || addrman.size() < 1000) + if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000) { pfrom->PushMessage(NetMsgType::GETADDR); pfrom->fGetAddr = true; } - addrman.Good(pfrom->addr); + connman.MarkAddressGood(pfrom->addr); } else { if (((CNetAddr)pfrom->addr) == (CNetAddr)addrFrom) { - addrman.Add(addrFrom, addrFrom); - addrman.Good(addrFrom); + connman.AddNewAddress(addrFrom, addrFrom); + connman.MarkAddressGood(addrFrom); } } @@ -5468,7 +5499,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vRecv >> vAddr; // Don't want addr from older versions unless seeding - if (pfrom->nVersion < CADDR_TIME_VERSION && addrman.size() > 1000) + if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000) return true; if (vAddr.size() > 1000) { @@ -5494,38 +5525,13 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, bool fReachable = IsReachable(addr); if (addr.nTime > nSince && !pfrom->fGetAddr && vAddr.size() <= 10 && addr.IsRoutable()) { - // Relay to a limited number of other nodes - { - LOCK(cs_vNodes); - // Use deterministic randomness to send to the same nodes for 24 hours - // at a time so the addrKnowns of the chosen nodes prevent repeats - static uint256 hashSalt; - if (hashSalt.IsNull()) - hashSalt = GetRandHash(); - uint64_t hashAddr = addr.GetHash(); - uint256 hashRand = ArithToUint256(UintToArith256(hashSalt) ^ (hashAddr<<32) ^ ((GetTime()+hashAddr)/(24*60*60))); - hashRand = Hash(BEGIN(hashRand), END(hashRand)); - multimap mapMix; - BOOST_FOREACH(CNode* pnode, vNodes) - { - if (pnode->nVersion < CADDR_TIME_VERSION) - continue; - unsigned int nPointer; - memcpy(&nPointer, &pnode, sizeof(nPointer)); - uint256 hashKey = ArithToUint256(UintToArith256(hashRand) ^ nPointer); - hashKey = Hash(BEGIN(hashKey), END(hashKey)); - mapMix.insert(make_pair(hashKey, pnode)); - } - int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) - for (multimap::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi) - ((*mi).second)->PushAddress(addr); - } + RelayAddress(addr, fReachable, connman); } // Do not store addresses outside our network if (fReachable) vAddrOk.push_back(addr); } - addrman.Add(vAddrOk, pfrom->addr, 2 * 60 * 60); + connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) @@ -5610,7 +5616,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Track requests for our stuff GetMainSignals().Inventory(inv.hash); - if (pfrom->nSendSize > (SendBufferSize() * 2)) { + if (pfrom->nSendSize > (nMaxSendBufferSize * 2)) { Misbehaving(pfrom->GetId(), 50); return error("send buffer size() = %u", pfrom->nSendSize); } @@ -5639,7 +5645,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("net", "received getdata for: %s peer=%d\n", vInv[0].ToString(), pfrom->id); pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom, chainparams.GetConsensus()); + ProcessGetData(pfrom, chainparams.GetConsensus(), connman); } @@ -5827,7 +5833,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } mempool.check(pcoinsTip); - RelayTransaction(tx); + connman.RelayTransaction(tx); vWorkQueue.push_back(inv.hash); pfrom->nLastTXTime = GetTime(); @@ -5863,7 +5869,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); - RelayTransaction(orphanTx); + connman.RelayTransaction(orphanTx); vWorkQueue.push_back(orphanHash); vEraseQueue.push_back(orphanHash); } @@ -5914,7 +5920,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, instantsend.RejectLockRequest(txLockRequest); // this lets other nodes to create lock request candidate i.e. // this allows multiple conflicting lock requests to compete for votes - RelayTransaction(tx); + connman.RelayTransaction(tx); } if (pfrom->fWhitelisted && GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) { @@ -5929,7 +5935,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS = 0; if (!state.IsInvalid(nDoS) || nDoS == 0) { LogPrintf("Force relaying tx %s from whitelisted peer=%d\n", tx.GetHash().ToString(), pfrom->id); - RelayTransaction(tx); + connman.RelayTransaction(tx); } else { LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state)); } @@ -6075,7 +6081,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Such an unrequested block may still be processed, subject to the // conditions in AcceptBlock(). bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); - ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL); + ProcessNewBlock(state, chainparams, pfrom, &block, forceProcessing, NULL, &connman); int nDoS; if (state.IsInvalid(nDoS)) { assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes @@ -6103,7 +6109,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } pfrom->vAddrToSend.clear(); - vector vAddr = addrman.GetAddr(); + vector vAddr = connman.GetAddresses(); BOOST_FOREACH(const CAddress &addr, vAddr) pfrom->PushAddress(addr); } @@ -6111,7 +6117,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if (strCommand == NetMsgType::MEMPOOL) { - if (CNode::OutboundTargetReached(false) && !pfrom->fWhitelisted) + if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint("net", "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; @@ -6233,9 +6239,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Relay pfrom->setKnown.insert(alertHash); { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + connman.ForEachNode([&alert](CNode* pnode) { alert.RelayTo(pnode); + }); } } else { @@ -6363,9 +6369,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } // requires LOCK(cs_vRecvMsg) -bool ProcessMessages(CNode* pfrom) +bool ProcessMessages(CNode* pfrom, CConnman& connman) { const CChainParams& chainparams = Params(); + unsigned int nMaxSendBufferSize = connman.GetSendBufferSize(); //if (fDebug) // LogPrintf("%s(%u messages)\n", __func__, pfrom->vRecvMsg.size()); @@ -6380,7 +6387,7 @@ bool ProcessMessages(CNode* pfrom) bool fOk = true; if (!pfrom->vRecvGetData.empty()) - ProcessGetData(pfrom, chainparams.GetConsensus()); + ProcessGetData(pfrom, chainparams.GetConsensus(), connman); // this maintains the order of responses if (!pfrom->vRecvGetData.empty()) return fOk; @@ -6388,7 +6395,7 @@ bool ProcessMessages(CNode* pfrom) std::deque::iterator it = pfrom->vRecvMsg.begin(); while (!pfrom->fDisconnect && it != pfrom->vRecvMsg.end()) { // Don't bother if send buffer is too full to respond anyway - if (pfrom->nSendSize >= SendBufferSize()) + if (pfrom->nSendSize >= nMaxSendBufferSize) break; // get next message @@ -6440,7 +6447,7 @@ bool ProcessMessages(CNode* pfrom) bool fRet = false; try { - fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime); + fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime, connman); boost::this_thread::interruption_point(); } catch (const std::ios_base::failure& e) @@ -6484,7 +6491,7 @@ bool ProcessMessages(CNode* pfrom) } -bool SendMessages(CNode* pto) +bool SendMessages(CNode* pto, CConnman& connman) { const Consensus::Params& consensusParams = Params().GetConsensus(); { @@ -6568,7 +6575,7 @@ bool SendMessages(CNode* pto) LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString()); else { - CNode::Ban(pto->addr, BanReasonNodeMisbehaving); + connman.Ban(pto->addr, BanReasonNodeMisbehaving); } } state.fShouldBan = false; @@ -6607,7 +6614,7 @@ bool SendMessages(CNode* pto) // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - GetMainSignals().Broadcast(nTimeBestReceived); + GetMainSignals().Broadcast(nTimeBestReceived, &connman); } // diff --git a/src/main.h b/src/main.h index 71d951433..f9b097106 100644 --- a/src/main.h +++ b/src/main.h @@ -36,6 +36,7 @@ class CBlockTreeDB; class CBloomFilter; class CChainParams; class CInv; +class CConnman; class CScriptCheck; class CTxMemPool; class CValidationInterface; @@ -205,7 +206,7 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals); * @param[out] dbp The already known disk position of pblock, or NULL if not yet stored. * @return True if state.IsValid() */ -bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp); +bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, CNode* pfrom, const CBlock* pblock, bool fForceProcessing, const CDiskBlockPos* dbp, CConnman* connman); /** Check whether enough disk space is available for an incoming block */ bool CheckDiskSpace(uint64_t nAdditionalBytes = 0); /** Open a block file (blk?????.dat) */ @@ -223,13 +224,14 @@ bool LoadBlockIndex(); /** Unload database information */ void UnloadBlockIndex(); /** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom); +bool ProcessMessages(CNode* pfrom, CConnman& connman); /** * Send queued protocol messages to be sent to a give node. * * @param[in] pto The node which we are sending messages to. + * @param[in] connman The connection manager for that node. */ -bool SendMessages(CNode* pto); +bool SendMessages(CNode* pto, CConnman& connman); /** Run an instance of the script checking thread */ void ThreadScriptCheck(); /** Check whether we are doing an initial block download (synchronizing from disk or network) */ @@ -245,7 +247,7 @@ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, const Consensus::Params& params, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL); +bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, const CBlock* pblock = NULL, CConnman* connman = NULL); double ConvertBitsToDouble(unsigned int nBits); CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false); diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 610009b1c..9ad0bdc9c 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -781,7 +781,7 @@ void CMasternodePaymentVote::Relay() // do not relay until synced if (!masternodeSync.IsWinnersListSynced()) return; CInv inv(MSG_MASTERNODE_PAYMENT_VOTE, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } bool CMasternodePaymentVote::CheckSignature(const CPubKey& pubKeyMasternode, int nValidationHeight, int &nDos) diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index cb527d1b4..5331c35ac 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -90,7 +90,7 @@ bool CMasternodeSync::IsBlockchainSynced(bool fBlockAccepted) if(fCheckpointsEnabled && pCurrentBlockIndex->nHeight < Checkpoints::GetTotalBlocksEstimate(Params().Checkpoints())) return false; - std::vector vNodesCopy = CopyNodeVector(); + std::vector vNodesCopy = g_connman->CopyNodeVector(); // We have enough peers and assume most of them are synced if(vNodesCopy.size() >= MASTERNODE_SYNC_ENOUGH_PEERS) { @@ -105,12 +105,12 @@ bool CMasternodeSync::IsBlockchainSynced(bool fBlockAccepted) if(nNodesAtSameHeight >= MASTERNODE_SYNC_ENOUGH_PEERS) { LogPrintf("CMasternodeSync::IsBlockchainSynced -- found enough peers on the same height as we are, done\n"); fBlockchainSynced = true; - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return true; } } } - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); // wait for at least one new block to be accepted if(!fFirstBlockAccepted) return false; @@ -192,12 +192,13 @@ void CMasternodeSync::SwitchToNextAsset() //try to activate our masternode if possible activeMasternode.ManageState(); - TRY_LOCK(cs_vNodes, lockRecv); - if(lockRecv) { - BOOST_FOREACH(CNode* pnode, vNodes) { - netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); - } - } + // TODO: Find out whether we can just use LOCK instead of: + // TRY_LOCK(cs_vNodes, lockRecv); + // if(lockRecv) { ... } + + g_connman->ForEachNode([](CNode* pnode) { + netfulfilledman.AddFulfilledRequest(pnode->addr, "full-sync"); + }); LogPrintf("CMasternodeSync::SwitchToNextAsset -- Sync has finished\n"); break; @@ -237,17 +238,17 @@ void CMasternodeSync::ProcessMessage(CNode* pfrom, std::string& strCommand, CDat void CMasternodeSync::ClearFulfilledRequests() { - TRY_LOCK(cs_vNodes, lockRecv); - if(!lockRecv) return; + // TODO: Find out whether we can just use LOCK instead of: + // TRY_LOCK(cs_vNodes, lockRecv); + // if(!lockRecv) return; - BOOST_FOREACH(CNode* pnode, vNodes) - { + g_connman->ForEachNode([](CNode* pnode) { netfulfilledman.RemoveFulfilledRequest(pnode->addr, "spork-sync"); netfulfilledman.RemoveFulfilledRequest(pnode->addr, "masternode-list-sync"); netfulfilledman.RemoveFulfilledRequest(pnode->addr, "masternode-payment-sync"); netfulfilledman.RemoveFulfilledRequest(pnode->addr, "governance-sync"); netfulfilledman.RemoveFulfilledRequest(pnode->addr, "full-sync"); - } + }); } void CMasternodeSync::ProcessTick() @@ -271,9 +272,9 @@ void CMasternodeSync::ProcessTick() LogPrintf("CMasternodeSync::ProcessTick -- WARNING: not enough data, restarting sync\n"); Reset(); } else { - std::vector vNodesCopy = CopyNodeVector(); + std::vector vNodesCopy = g_connman->CopyNodeVector(); governance.RequestGovernanceObjectVotes(vNodesCopy); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } } @@ -309,7 +310,7 @@ void CMasternodeSync::ProcessTick() SwitchToNextAsset(); } - std::vector vNodesCopy = CopyNodeVector(); + std::vector vNodesCopy = g_connman->CopyNodeVector(); BOOST_FOREACH(CNode* pnode, vNodesCopy) { @@ -334,7 +335,7 @@ void CMasternodeSync::ProcessTick() nRequestedMasternodeAssets = MASTERNODE_SYNC_FINISHED; } nRequestedMasternodeAttempt++; - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } @@ -370,11 +371,11 @@ void CMasternodeSync::ProcessTick() LogPrintf("CMasternodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); // there is no way we can continue without masternode list, fail here and try later Fail(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } SwitchToNextAsset(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } @@ -387,7 +388,7 @@ void CMasternodeSync::ProcessTick() mnodeman.DsegUpdate(pnode); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; //this will cause each peer to get one request each six seconds for the various assets we need } @@ -404,11 +405,11 @@ void CMasternodeSync::ProcessTick() LogPrintf("CMasternodeSync::ProcessTick -- ERROR: failed to sync %s\n", GetAssetName()); // probably not a good idea to proceed without winner list Fail(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } SwitchToNextAsset(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } @@ -418,7 +419,7 @@ void CMasternodeSync::ProcessTick() if(nRequestedMasternodeAttempt > 1 && mnpayments.IsEnoughData()) { LogPrintf("CMasternodeSync::ProcessTick -- nTick %d nRequestedMasternodeAssets %d -- found enough data\n", nTick, nRequestedMasternodeAssets); SwitchToNextAsset(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } @@ -434,7 +435,7 @@ void CMasternodeSync::ProcessTick() // ask node for missing pieces only (old nodes will not be asked) mnpayments.RequestLowDataPaymentBlocks(pnode); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; //this will cause each peer to get one request each six seconds for the various assets we need } @@ -451,7 +452,7 @@ void CMasternodeSync::ProcessTick() // it's kind of ok to skip this for now, hopefully we'll catch up later? } SwitchToNextAsset(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } @@ -480,7 +481,7 @@ void CMasternodeSync::ProcessTick() // reset nTimeNoObjectsLeft to be able to use the same condition on resync nTimeNoObjectsLeft = 0; SwitchToNextAsset(); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; } nLastTick = nTick; @@ -495,13 +496,13 @@ void CMasternodeSync::ProcessTick() SendGovernanceSyncRequest(pnode); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return; //this will cause each peer to get one request each six seconds for the various assets we need } } } // looped through all nodes, release them - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); } void CMasternodeSync::SendGovernanceSyncRequest(CNode* pnode) diff --git a/src/masternode.cpp b/src/masternode.cpp index 803985772..6227a9a48 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -756,7 +756,7 @@ bool CMasternodeBroadcast::CheckSignature(int& nDos) void CMasternodeBroadcast::Relay() { CInv inv(MSG_MASTERNODE_ANNOUNCE, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } CMasternodePing::CMasternodePing(CTxIn& vinNew) : @@ -916,7 +916,7 @@ bool CMasternodePing::CheckAndUpdate(CMasternode* pmn, bool fFromNewBroadcast, i void CMasternodePing::Relay() { CInv inv(MSG_MASTERNODE_PING, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } void CMasternode::AddGovernanceVote(uint256 nGovernanceObjectHash) diff --git a/src/masternode.h b/src/masternode.h index 08e198c07..ddca731a8 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -472,7 +472,7 @@ public: void Relay() const { CInv inv(MSG_MASTERNODE_VERIFY, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } }; diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index 7f3ad1480..b4dd7cfc7 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -763,14 +763,13 @@ void CMasternodeMan::ProcessMasternodeConnections() //we don't care about this for regtest if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return; - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) { + g_connman->ForEachNode([](CNode* pnode) { if(pnode->fMasternode) { - if(privateSendClient.infoMixingMasternode.fInfoValid && pnode->addr == privateSendClient.infoMixingMasternode.addr) continue; + if(privateSendClient.infoMixingMasternode.fInfoValid && pnode->addr == privateSendClient.infoMixingMasternode.addr) return true; LogPrintf("Closing Masternode connection: peer=%d, addr=%s\n", pnode->id, pnode->addr.ToString()); pnode->fDisconnect = true; } - } + }); } std::pair > CMasternodeMan::PopScheduledMnbRequestConnection() @@ -819,7 +818,7 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData if (CheckMnbAndUpdateMasternodeList(pfrom, mnb, nDos)) { // use announced Masternode as a peer - addrman.Add(CAddress(mnb.addr, NODE_NETWORK), pfrom->addr, 2*60*60); + g_connman->AddNewAddress(CAddress(mnb.addr, NODE_NETWORK), pfrom->addr, 2*60*60); } else if(nDos > 0) { Misbehaving(pfrom->GetId(), nDos); } @@ -1098,7 +1097,8 @@ bool CMasternodeMan::SendVerifyRequest(const CAddress& addr, const std::vectorConnectNode(addr, NULL, true); if(pnode == NULL) { LogPrintf("CMasternodeMan::SendVerifyRequest -- can't connect to node to verify it, addr=%s\n", addr.ToString()); return false; diff --git a/src/miner.cpp b/src/miner.cpp index 5134ce83f..468640467 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -376,7 +376,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned // } //} -static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams) +static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams, CConnman* connman) { LogPrintf("%s\n", pblock->ToString()); LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue)); @@ -393,14 +393,14 @@ static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainpar // Process this block the same as if we had received it from another node CValidationState state; - if (!ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)) + if (!ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, connman)) return error("ProcessBlockFound -- ProcessNewBlock() failed, block not accepted"); return true; } // ***TODO*** that part changed in bitcoin, we are using a mix with old one here for now -void static BitcoinMiner(const CChainParams& chainparams) +void static BitcoinMiner(const CChainParams& chainparams, CConnman& connman) { LogPrintf("DashMiner -- started\n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -423,11 +423,7 @@ void static BitcoinMiner(const CChainParams& chainparams) // Busy-wait for the network to come online so we don't waste time mining // on an obsolete chain. In regtest mode we expect to fly solo. do { - bool fvNodesEmpty; - { - LOCK(cs_vNodes); - fvNodesEmpty = vNodes.empty(); - } + bool fvNodesEmpty = connman.GetNodeCount(CConnman::CONNECTIONS_ALL) == 0; if (!fvNodesEmpty && !IsInitialBlockDownload() && masternodeSync.IsSynced()) break; MilliSleep(1000); @@ -472,7 +468,7 @@ void static BitcoinMiner(const CChainParams& chainparams) // Found a solution SetThreadPriority(THREAD_PRIORITY_NORMAL); LogPrintf("DashMiner:\n proof-of-work found\n hash: %s\n target: %s\n", hash.GetHex(), hashTarget.GetHex()); - ProcessBlockFound(pblock, chainparams); + ProcessBlockFound(pblock, chainparams, &connman); SetThreadPriority(THREAD_PRIORITY_LOWEST); coinbaseScript->KeepScript(); @@ -492,7 +488,7 @@ void static BitcoinMiner(const CChainParams& chainparams) // Check for stop or if block needs to be rebuilt boost::this_thread::interruption_point(); // Regtest mode doesn't require peers - if (vNodes.empty() && chainparams.MiningRequiresPeers()) + if (connman.GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && chainparams.MiningRequiresPeers()) break; if (pblock->nNonce >= 0xffff0000) break; @@ -525,7 +521,7 @@ void static BitcoinMiner(const CChainParams& chainparams) } } -void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams) +void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams, CConnman& connman) { static boost::thread_group* minerThreads = NULL; @@ -544,5 +540,5 @@ void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainpar minerThreads = new boost::thread_group(); for (int i = 0; i < nThreads; i++) - minerThreads->create_thread(boost::bind(&BitcoinMiner, boost::cref(chainparams))); + minerThreads->create_thread(boost::bind(&BitcoinMiner, boost::cref(chainparams), boost::ref(connman))); } diff --git a/src/miner.h b/src/miner.h index 512494198..1b0584259 100644 --- a/src/miner.h +++ b/src/miner.h @@ -12,6 +12,7 @@ class CBlockIndex; class CChainParams; +class CConnman; class CReserveKey; class CScript; class CWallet; @@ -30,7 +31,7 @@ struct CBlockTemplate }; /** Run the miner threads */ -void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams); +void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams, CConnman& connman); /** Generate a new block, without valid proof-of-work */ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn); /** Modify the extranonce in a block */ diff --git a/src/net.cpp b/src/net.cpp index c4959c63c..06a2dadfc 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -66,67 +66,29 @@ #endif #endif - -namespace { - const int MAX_OUTBOUND_CONNECTIONS = 8; - const int MAX_OUTBOUND_MASTERNODE_CONNECTIONS = 20; - const int MAX_FEELER_CONNECTIONS = 1; - - struct ListenSocket { - SOCKET socket; - bool whitelisted; - - ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {} - }; -} - const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; -/** Services this node implementation cares about */ -static const ServiceFlags nRelevantServices = NODE_NETWORK; - // // Global state variables // bool fDiscover = true; bool fListen = true; -ServiceFlags nLocalServices = NODE_NETWORK; CCriticalSection cs_mapLocalHost; std::map mapLocalHost; static bool vfLimited[NET_MAX] = {}; static CNode* pnodeLocalHost = NULL; -uint64_t nLocalHostNonce = 0; -static std::vector vhListenSocket; -CAddrMan addrman; -int nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; -bool fAddressesInitialized = false; std::string strSubVersion; -std::vector vNodes; -CCriticalSection cs_vNodes; std::map mapRelay; std::deque > vRelayExpiration; CCriticalSection cs_mapRelay; limitedmap mapAlreadyAskedFor(MAX_INV_SZ); -static std::deque vOneShots; -CCriticalSection cs_vOneShots; - -std::vector vAddedNodes; -CCriticalSection cs_vAddedNodes; - -NodeId nLastNodeId = 0; -CCriticalSection cs_nLastNodeId; - -static CSemaphore *semOutbound = NULL; -static CSemaphore *semMasternodeOutbound = NULL; -boost::condition_variable messageHandlerCondition; - // Signals for message handling static CNodeSignals g_signals; CNodeSignals& GetNodeSignals() { return g_signals; } -void AddOneShot(const std::string& strDest) +void CConnman::AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); vOneShots.push_back(strDest); @@ -187,7 +149,7 @@ static std::vector convertSeed6(const std::vector &vSeedsIn // Otherwise, return the unroutable 0.0.0.0 but filled in with // the normal parameters, since the IP may be changed to a useful // one by discovery. -CAddress GetLocalAddress(const CNetAddr *paddrPeer) +CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices) { CAddress ret(CService("0.0.0.0",GetListenPort()), NODE_NONE); CService addr; @@ -219,7 +181,7 @@ void AdvertiseLocal(CNode *pnode) { if (fListen && pnode->fSuccessfullyConnected) { - CAddress addrLocal = GetLocalAddress(&pnode->addr); + CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices()); // If discovery is enabled, sometimes give our peer the address it // tells us that it sees us as in case it has a better idea of our // address than we do. @@ -330,25 +292,10 @@ bool IsReachable(const CNetAddr& addr) return IsReachable(net); } -void AddressCurrentlyConnected(const CService& addr) -{ - addrman.Connected(addr); -} - - -uint64_t CNode::nTotalBytesRecv = 0; -uint64_t CNode::nTotalBytesSent = 0; -CCriticalSection CNode::cs_totalBytesRecv; -CCriticalSection CNode::cs_totalBytesSent; - -uint64_t CNode::nMaxOutboundLimit = 0; -uint64_t CNode::nMaxOutboundTotalBytesSentInCycle = 0; -uint64_t CNode::nMaxOutboundTimeframe = 60*60*24; //1 day -uint64_t CNode::nMaxOutboundCycleStartTime = 0; std::vector CNode::vchSecretKey; -CNode* FindNode(const CNetAddr& ip) +CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -357,7 +304,7 @@ CNode* FindNode(const CNetAddr& ip) return NULL; } -CNode* FindNode(const CSubNet& subNet) +CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -366,7 +313,7 @@ CNode* FindNode(const CSubNet& subNet) return NULL; } -CNode* FindNode(const std::string& addrName) +CNode* CConnman::FindNode(const std::string& addrName) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -375,7 +322,7 @@ CNode* FindNode(const std::string& addrName) return NULL; } -CNode* FindNode(const CService& addr) +CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -384,7 +331,17 @@ CNode* FindNode(const CService& addr) return NULL; } -CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fConnectToMasternode) +bool CConnman::CheckIncomingNonce(uint64_t nonce) +{ + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) { + if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce) + return false; + } + return true; +} + +CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fConnectToMasternode) { if (pszDest == NULL) { // we clean masternode connections in CMasternodeMan::ProcessMasternodeConnections() @@ -452,7 +409,8 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fConnectToMas addrman.Attempt(addrConnect); // Add node - CNode* pnode = new CNode(hSocket, addrConnect, pszDest ? pszDest : "", false, true); + CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addrConnect, pszDest ? pszDest : "", false, true); + GetNodeSignals().InitializeNode(pnode->GetId(), pnode); pnode->nTimeConnected = GetTime(); if(fConnectToMasternode) { @@ -474,21 +432,21 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fConnectToMas return NULL; } -static void DumpBanlist() +void CConnman::DumpBanlist() { - CNode::SweepBanned(); // clean unused entries (if bantime has expired) + SweepBanned(); // clean unused entries (if bantime has expired) - if (!CNode::BannedSetIsDirty()) + if (!BannedSetIsDirty()) return; int64_t nStart = GetTimeMillis(); CBanDB bandb; banmap_t banmap; - CNode::SetBannedSetDirty(false); - CNode::GetBanned(banmap); + SetBannedSetDirty(false); + GetBanned(banmap); if (!bandb.Write(banmap)) - CNode::SetBannedSetDirty(true); + SetBannedSetDirty(true); LogPrint("net", "Flushed %d banned node ips/subnets to banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); @@ -511,29 +469,22 @@ void CNode::CloseSocketDisconnect() void CNode::PushVersion() { - int nBestHeight = GetNodeSignals().GetHeight().get_value_or(0); - int64_t nTime = (fInbound ? GetAdjustedTime() : GetTime()); CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService("0.0.0.0", 0), addr.nServices)); - CAddress addrMe = GetLocalAddress(&addr); - GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + CAddress addrMe = GetLocalAddress(&addr, nLocalServices); if (fLogIPs) - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), addrYou.ToString(), id); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, them=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), addrYou.ToString(), id); else - LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nBestHeight, addrMe.ToString(), id); + LogPrint("net", "send version message: version %d, blocks=%d, us=%s, peer=%d\n", PROTOCOL_VERSION, nMyStartingHeight, addrMe.ToString(), id); PushMessage(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalServices, nTime, addrYou, addrMe, - nLocalHostNonce, strSubVersion, nBestHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); + nLocalHostNonce, strSubVersion, nMyStartingHeight, !GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)); } -banmap_t CNode::setBanned; -CCriticalSection CNode::cs_setBanned; -bool CNode::setBannedIsDirty; - -void CNode::ClearBanned() +void CConnman::ClearBanned() { { LOCK(cs_setBanned); @@ -541,10 +492,11 @@ void CNode::ClearBanned() setBannedIsDirty = true; } DumpBanlist(); //store banlist to disk - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); } -bool CNode::IsBanned(CNetAddr ip) +bool CConnman::IsBanned(CNetAddr ip) { bool fResult = false; { @@ -561,7 +513,7 @@ bool CNode::IsBanned(CNetAddr ip) return fResult; } -bool CNode::IsBanned(CSubNet subnet) +bool CConnman::IsBanned(CSubNet subnet) { bool fResult = false; { @@ -577,12 +529,12 @@ bool CNode::IsBanned(CSubNet subnet) return fResult; } -void CNode::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CConnman::Ban(const CNetAddr& addr, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CSubNet subNet(addr); Ban(subNet, banReason, bantimeoffset, sinceUnixEpoch); } -void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { +void CConnman::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t bantimeoffset, bool sinceUnixEpoch) { CBanEntry banEntry(GetTime()); banEntry.banReason = banReason; if (bantimeoffset <= 0) @@ -601,7 +553,8 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti else return; } - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { @@ -613,37 +566,38 @@ void CNode::Ban(const CSubNet& subNet, const BanReason &banReason, int64_t banti DumpBanlist(); //store banlist to disk immediately if user requested ban } -bool CNode::Unban(const CNetAddr &addr) { +bool CConnman::Unban(const CNetAddr &addr) { CSubNet subNet(addr); return Unban(subNet); } -bool CNode::Unban(const CSubNet &subNet) { +bool CConnman::Unban(const CSubNet &subNet) { { LOCK(cs_setBanned); if (!setBanned.erase(subNet)) return false; setBannedIsDirty = true; } - uiInterface.BannedListChanged(); + if(clientInterface) + clientInterface->BannedListChanged(); DumpBanlist(); //store banlist to disk immediately return true; } -void CNode::GetBanned(banmap_t &banMap) +void CConnman::GetBanned(banmap_t &banMap) { LOCK(cs_setBanned); banMap = setBanned; //create a thread safe copy } -void CNode::SetBanned(const banmap_t &banMap) +void CConnman::SetBanned(const banmap_t &banMap) { LOCK(cs_setBanned); setBanned = banMap; setBannedIsDirty = true; } -void CNode::SweepBanned() +void CConnman::SweepBanned() { int64_t now = GetTime(); @@ -664,23 +618,20 @@ void CNode::SweepBanned() } } -bool CNode::BannedSetIsDirty() +bool CConnman::BannedSetIsDirty() { LOCK(cs_setBanned); return setBannedIsDirty; } -void CNode::SetBannedSetDirty(bool dirty) +void CConnman::SetBannedSetDirty(bool dirty) { LOCK(cs_setBanned); //reuse setBanned lock for the isDirty flag setBannedIsDirty = dirty; } -std::vector CNode::vWhitelistedRange; -CCriticalSection CNode::cs_vWhitelistedRange; - -bool CNode::IsWhitelistedRange(const CNetAddr &addr) { +bool CConnman::IsWhitelistedRange(const CNetAddr &addr) { LOCK(cs_vWhitelistedRange); BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) { if (subnet.Match(addr)) @@ -689,7 +640,7 @@ bool CNode::IsWhitelistedRange(const CNetAddr &addr) { return false; } -void CNode::AddWhitelistedRange(const CSubNet &subnet) { +void CConnman::AddWhitelistedRange(const CSubNet &subnet) { LOCK(cs_vWhitelistedRange); vWhitelistedRange.push_back(subnet); } @@ -738,8 +689,9 @@ void CNode::copyStats(CNodeStats &stats) #undef X // requires LOCK(cs_vRecvMsg) -bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) +bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete) { + complete = false; while (nBytes > 0) { // get current incomplete message, or create a new one @@ -778,7 +730,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; msg.nTime = GetTimeMicros(); - messageHandlerCondition.notify_one(); + complete = true; } } @@ -841,9 +793,10 @@ int CNetMessage::readData(const char *pch, unsigned int nBytes) // requires LOCK(cs_vSend) -void SocketSendData(CNode *pnode) +size_t SocketSendData(CNode *pnode) { std::deque::iterator it = pnode->vSendMsg.begin(); + size_t nSentSize = 0; while (it != pnode->vSendMsg.end()) { const CSerializeData &data = *it; @@ -853,7 +806,7 @@ void SocketSendData(CNode *pnode) pnode->nLastSend = GetTime(); pnode->nSendBytes += nBytes; pnode->nSendOffset += nBytes; - pnode->RecordBytesSent(nBytes); + nSentSize += nBytes; if (pnode->nSendOffset == data.size()) { pnode->nSendOffset = 0; pnode->nSendSize -= data.size(); @@ -882,10 +835,9 @@ void SocketSendData(CNode *pnode) assert(pnode->nSendSize == 0); } pnode->vSendMsg.erase(pnode->vSendMsg.begin(), it); + return nSentSize; } -static std::list vNodesDisconnected; - struct NodeEvictionCandidate { NodeEvictionCandidate(CNode* pnode) @@ -953,7 +905,8 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction * to forge. In order to partition a node the attacker must be * simultaneously better at all of them than honest peers. */ -static bool AttemptToEvictConnection() { +bool CConnman::AttemptToEvictConnection() +{ std::vector vEvictionCandidates; { LOCK(cs_vNodes); @@ -1051,20 +1004,20 @@ static bool AttemptToEvictConnection() { return false; } -static void AcceptConnection(const ListenSocket& hListenSocket) { +void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len); CAddress addr; int nInbound = 0; - int nMaxInbound = nMaxConnections - (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); + int nMaxInbound = nMaxConnections - (nMaxOutbound + nMaxFeeler); assert(nMaxInbound > 0); if (hSocket != INVALID_SOCKET) if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) LogPrintf("Warning: Unknown socket family\n"); - bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr); + bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) @@ -1096,7 +1049,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int)); #endif - if (CNode::IsBanned(addr) && !whitelisted) + if (IsBanned(addr) && !whitelisted) { LogPrintf("connection from %s dropped (banned)\n", addr.ToString()); CloseSocket(hSocket); @@ -1120,7 +1073,8 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { return; } - CNode* pnode = new CNode(hSocket, addr, "", true); + CNode* pnode = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), hSocket, addr, "", true); + GetNodeSignals().InitializeNode(pnode->GetId(), pnode); pnode->fWhitelisted = whitelisted; LogPrint("net", "connection from %s accepted\n", addr.ToString()); @@ -1131,7 +1085,7 @@ static void AcceptConnection(const ListenSocket& hListenSocket) { } } -void ThreadSocketHandler() +void CConnman::ThreadSocketHandler() { unsigned int nPrevNodeCount = 0; while (true) @@ -1195,14 +1149,15 @@ void ThreadSocketHandler() if (fDelete) { vNodesDisconnected.remove(pnode); - delete pnode; + DeleteNode(pnode); } } } } if(vNodes.size() != nPrevNodeCount) { nPrevNodeCount = vNodes.size(); - uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount); + if(clientInterface) + clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount); } // @@ -1254,16 +1209,22 @@ void ThreadSocketHandler() // * We process a message in the buffer (message handler thread). { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend && !pnode->vSendMsg.empty()) { - FD_SET(pnode->hSocket, &fdsetSend); - continue; + if (lockSend) { + if (pnode->nOptimisticBytesWritten) { + RecordBytesSent(pnode->nOptimisticBytesWritten); + pnode->nOptimisticBytesWritten = 0; + } + if (!pnode->vSendMsg.empty()) { + FD_SET(pnode->hSocket, &fdsetSend); + continue; + } } } { TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv && ( pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() || - pnode->GetTotalRecvSize() <= ReceiveFloodSize())) + pnode->GetTotalRecvSize() <= GetReceiveFloodSize())) FD_SET(pnode->hSocket, &fdsetRecv); } } @@ -1322,11 +1283,14 @@ void ThreadSocketHandler() int nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT); if (nBytes > 0) { - if (!pnode->ReceiveMsgBytes(pchBuf, nBytes)) + bool notify = false; + if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify)) pnode->CloseSocketDisconnect(); + if(notify) + messageHandlerCondition.notify_one(); pnode->nLastRecv = GetTime(); pnode->nRecvBytes += nBytes; - pnode->RecordBytesRecv(nBytes); + RecordBytesRecv(nBytes); } else if (nBytes == 0) { @@ -1358,8 +1322,11 @@ void ThreadSocketHandler() if (FD_ISSET(pnode->hSocket, &fdsetSend)) { TRY_LOCK(pnode->cs_vSend, lockSend); - if (lockSend) - SocketSendData(pnode); + if (lockSend) { + size_t nBytes = SocketSendData(pnode); + if (nBytes) + RecordBytesSent(nBytes); + } } // @@ -1520,7 +1487,7 @@ void MapPort(bool) -void ThreadDNSAddressSeed() +void CConnman::ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute if ((addrman.size() > 0) && @@ -1574,7 +1541,7 @@ void ThreadDNSAddressSeed() -void DumpAddresses() +void CConnman::DumpAddresses() { int64_t nStart = GetTimeMillis(); @@ -1585,13 +1552,13 @@ void DumpAddresses() addrman.size(), GetTimeMillis() - nStart); } -void DumpData() +void CConnman::DumpData() { DumpAddresses(); DumpBanlist(); } -void static ProcessOneShot() +void CConnman::ProcessOneShot() { std::string strDest; { @@ -1609,7 +1576,7 @@ void static ProcessOneShot() } } -void ThreadOpenConnections() +void CConnman::ThreadOpenConnections() { // Connect to specific addresses if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) @@ -1672,7 +1639,7 @@ void ThreadOpenConnections() } } } - assert(nOutbound <= (MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS)); + assert(nOutbound <= (nMaxOutbound + nMaxFeeler)); // Feeler Connections // @@ -1687,7 +1654,7 @@ void ThreadOpenConnections() // * Only make a feeler connection once every few minutes. // bool fFeeler = false; - if (nOutbound >= MAX_OUTBOUND_CONNECTIONS) { + if (nOutbound >= nMaxOutbound) { int64_t nTime = GetTimeMicros(); // The current time right now (in microseconds). if (nTime > nNextFeeler) { nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL); @@ -1747,7 +1714,7 @@ void ThreadOpenConnections() } } -std::vector GetAddedNodeInfo() +std::vector CConnman::GetAddedNodeInfo() { std::vector ret; @@ -1799,7 +1766,7 @@ std::vector GetAddedNodeInfo() return ret; } -void ThreadOpenAddedConnections() +void CConnman::ThreadOpenAddedConnections() { { LOCK(cs_vAddedNodes); @@ -1824,7 +1791,7 @@ void ThreadOpenAddedConnections() } } -void ThreadMnbRequestConnections() +void CConnman::ThreadMnbRequestConnections() { // Connecting to specific addresses, no masternode connections available if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) @@ -1869,7 +1836,7 @@ void ThreadMnbRequestConnections() } // if successful, this moves the passed grant to the constructed node -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler) +bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound, const char *pszDest, bool fOneShot, bool fFeeler) { // // Initiate outbound network connection @@ -1877,7 +1844,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu boost::this_thread::interruption_point(); if (!pszDest) { if (IsLocal(addrConnect) || - FindNode((CNetAddr)addrConnect) || CNode::IsBanned(addrConnect) || + FindNode((CNetAddr)addrConnect) || IsBanned(addrConnect) || FindNode(addrConnect.ToStringIPPort())) return false; } else if (FindNode(std::string(pszDest))) @@ -1899,7 +1866,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu } -void ThreadMessageHandler() +void CConnman::ThreadMessageHandler() { boost::mutex condition_mutex; boost::unique_lock lock(condition_mutex); @@ -1921,10 +1888,10 @@ void ThreadMessageHandler() TRY_LOCK(pnode->cs_vRecvMsg, lockRecv); if (lockRecv) { - if (!GetNodeSignals().ProcessMessages(pnode)) + if (!GetNodeSignals().ProcessMessages(pnode, *this)) pnode->fDisconnect = true; - if (pnode->nSendSize < SendBufferSize()) + if (pnode->nSendSize < GetSendBufferSize()) { if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete())) { @@ -1939,7 +1906,7 @@ void ThreadMessageHandler() { TRY_LOCK(pnode->cs_vSend, lockSend); if (lockSend) - GetNodeSignals().SendMessages(pnode); + GetNodeSignals().SendMessages(pnode, *this); } boost::this_thread::interruption_point(); } @@ -1956,7 +1923,7 @@ void ThreadMessageHandler() -bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) +bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted) { strError = ""; int nOne = 1; @@ -2054,7 +2021,7 @@ bool BindListenPort(const CService &addrBind, std::string& strError, bool fWhite return true; } -void static Discover(boost::thread_group& threadGroup) +void Discover(boost::thread_group& threadGroup) { if (!fDiscover) return; @@ -2105,9 +2072,49 @@ void static Discover(boost::thread_group& threadGroup) #endif } -void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) +CConnman::CConnman() { - uiInterface.InitMessage(_("Loading addresses...")); + setBannedIsDirty = false; + fAddressesInitialized = false; + nLastNodeId = 0; + nSendBufferMaxSize = 0; + nReceiveFloodSize = 0; + semOutbound = NULL; + semMasternodeOutbound = NULL; + nMaxConnections = 0; + nMaxOutbound = 0; + nBestHeight = 0; + clientInterface = NULL; +} + +NodeId CConnman::GetNewNodeId() +{ + return nLastNodeId.fetch_add(1, std::memory_order_relaxed); +} + +bool CConnman::Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options connOptions) +{ + nTotalBytesRecv = 0; + nTotalBytesSent = 0; + nMaxOutboundLimit = 0; + nMaxOutboundTotalBytesSentInCycle = 0; + nMaxOutboundTimeframe = 60*60*24; //1 day + nMaxOutboundCycleStartTime = 0; + + nRelevantServices = connOptions.nRelevantServices; + nLocalServices = connOptions.nLocalServices; + nMaxConnections = connOptions.nMaxConnections; + nMaxOutbound = std::min((connOptions.nMaxOutbound), nMaxConnections); + nMaxFeeler = connOptions.nMaxFeeler; + + nSendBufferMaxSize = connOptions.nSendBufferMaxSize; + nReceiveFloodSize = connOptions.nSendBufferMaxSize; + + SetBestHeight(connOptions.nBestHeight); + + clientInterface = connOptions.uiInterface; + if (clientInterface) + clientInterface->InitMessage(_("Loading addresses...")); // Load addresses from peers.dat int64_t nStart = GetTimeMillis(); { @@ -2120,22 +2127,22 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) DumpAddresses(); } } - - uiInterface.InitMessage(_("Loading banlist...")); + if (clientInterface) + clientInterface->InitMessage(_("Loading banlist...")); // Load addresses from banlist.dat nStart = GetTimeMillis(); CBanDB bandb; banmap_t banmap; if (bandb.Read(banmap)) { - CNode::SetBanned(banmap); // thread save setter - CNode::SetBannedSetDirty(false); // no need to write down, just read data - CNode::SweepBanned(); // sweep out unused entries + SetBanned(banmap); // thread save setter + SetBannedSetDirty(false); // no need to write down, just read data + SweepBanned(); // sweep out unused entries LogPrint("net", "Loaded %d banned node ips/subnets from banlist.dat %dms\n", banmap.size(), GetTimeMillis() - nStart); } else { LogPrintf("Invalid or missing banlist.dat; recreating\n"); - CNode::SetBannedSetDirty(true); // force write + SetBannedSetDirty(true); // force write DumpBanlist(); } @@ -2143,8 +2150,7 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) if (semOutbound == NULL) { // initialize semaphore - int nMaxOutbound = std::min((MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS), nMaxConnections); - semOutbound = new CSemaphore(nMaxOutbound); + semOutbound = new CSemaphore(std::min((nMaxOutbound + nMaxFeeler), nMaxConnections)); } if (semMasternodeOutbound == NULL) { @@ -2152,10 +2158,10 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) semMasternodeOutbound = new CSemaphore(MAX_OUTBOUND_MASTERNODE_CONNECTIONS); } - if (pnodeLocalHost == NULL) - pnodeLocalHost = new CNode(INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); - - Discover(threadGroup); + if (pnodeLocalHost == NULL) { + pnodeLocalHost = new CNode(GetNewNodeId(), nLocalServices, GetBestHeight(), INVALID_SOCKET, CAddress(CService("127.0.0.1", 0), nLocalServices)); + GetNodeSignals().InitializeNode(pnodeLocalHost->GetId(), pnodeLocalHost); + } // // Start threads @@ -2164,47 +2170,25 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) if (!GetBoolArg("-dnsseed", true)) LogPrintf("DNS seeding disabled\n"); else - threadGroup.create_thread(boost::bind(&TraceThread, "dnsseed", &ThreadDNSAddressSeed)); - - // Map ports with UPnP - MapPort(GetBoolArg("-upnp", DEFAULT_UPNP)); + threadGroup.create_thread(boost::bind(&TraceThread >, "dnsseed", boost::function(boost::bind(&CConnman::ThreadDNSAddressSeed, this)))); // Send and receive from sockets, accept connections - threadGroup.create_thread(boost::bind(&TraceThread, "net", &ThreadSocketHandler)); + threadGroup.create_thread(boost::bind(&TraceThread >, "net", boost::function(boost::bind(&CConnman::ThreadSocketHandler, this)))); // Initiate outbound connections from -addnode - threadGroup.create_thread(boost::bind(&TraceThread, "addcon", &ThreadOpenAddedConnections)); + threadGroup.create_thread(boost::bind(&TraceThread >, "addcon", boost::function(boost::bind(&CConnman::ThreadOpenAddedConnections, this)))); // Initiate outbound connections - threadGroup.create_thread(boost::bind(&TraceThread, "opencon", &ThreadOpenConnections)); + threadGroup.create_thread(boost::bind(&TraceThread >, "opencon", boost::function(boost::bind(&CConnman::ThreadOpenConnections, this)))); // Initiate masternode connections - threadGroup.create_thread(boost::bind(&TraceThread, "mnbcon", &ThreadMnbRequestConnections)); + threadGroup.create_thread(boost::bind(&TraceThread >, "mnbcon", boost::function(boost::bind(&CConnman::ThreadMnbRequestConnections, this)))); // Process messages - threadGroup.create_thread(boost::bind(&TraceThread, "msghand", &ThreadMessageHandler)); + threadGroup.create_thread(boost::bind(&TraceThread >, "msghand", boost::function(boost::bind(&CConnman::ThreadMessageHandler, this)))); // Dump network addresses - scheduler.scheduleEvery(&DumpData, DUMP_ADDRESSES_INTERVAL); -} - -bool StopNode() -{ - LogPrintf("StopNode()\n"); - MapPort(false); - if (semOutbound) - for (int i=0; i<(MAX_OUTBOUND_CONNECTIONS + MAX_FEELER_CONNECTIONS); i++) - semOutbound->post(); - - if (semMasternodeOutbound) - for (int i=0; ipost(); - - if (fAddressesInitialized) - { - DumpData(); - fAddressesInitialized = false; - } + scheduler.scheduleEvery(boost::bind(&CConnman::DumpData, this), DUMP_ADDRESSES_INTERVAL); return true; } @@ -2216,30 +2200,6 @@ public: ~CNetCleanup() { - // Close sockets - BOOST_FOREACH(CNode* pnode, vNodes) - if (pnode->hSocket != INVALID_SOCKET) - CloseSocket(pnode->hSocket); - BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) - if (hListenSocket.socket != INVALID_SOCKET) - if (!CloseSocket(hListenSocket.socket)) - LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); - - // clean up some globals (to help leak detection) - BOOST_FOREACH(CNode *pnode, vNodes) - delete pnode; - BOOST_FOREACH(CNode *pnode, vNodesDisconnected) - delete pnode; - vNodes.clear(); - vNodesDisconnected.clear(); - vhListenSocket.clear(); - delete semOutbound; - semOutbound = NULL; - delete semMasternodeOutbound; - semMasternodeOutbound = NULL; - delete pnodeLocalHost; - pnodeLocalHost = NULL; - #ifdef WIN32 // Shutdown Windows Sockets WSACleanup(); @@ -2256,7 +2216,185 @@ void CExplicitNetCleanup::callCleanup() delete tmp; // Stroustrup's gonna kill me for that } -void RelayTransaction(const CTransaction& tx) +void CConnman::Stop() +{ + LogPrintf("%s\n",__func__); + if (semOutbound) + for (int i=0; i<(nMaxOutbound + nMaxFeeler); i++) + semOutbound->post(); + + if (semMasternodeOutbound) + for (int i=0; ipost(); + + if (fAddressesInitialized) + { + DumpData(); + fAddressesInitialized = false; + } + + // Close sockets + BOOST_FOREACH(CNode* pnode, vNodes) + if (pnode->hSocket != INVALID_SOCKET) + CloseSocket(pnode->hSocket); + BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket) + if (hListenSocket.socket != INVALID_SOCKET) + if (!CloseSocket(hListenSocket.socket)) + LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError())); + + // clean up some globals (to help leak detection) + BOOST_FOREACH(CNode *pnode, vNodes) { + DeleteNode(pnode); + } + BOOST_FOREACH(CNode *pnode, vNodesDisconnected) { + DeleteNode(pnode); + } + vNodes.clear(); + vNodesDisconnected.clear(); + vhListenSocket.clear(); + delete semOutbound; + semOutbound = NULL; + delete semMasternodeOutbound; + semMasternodeOutbound = NULL; + if(pnodeLocalHost) + DeleteNode(pnodeLocalHost); + pnodeLocalHost = NULL; +} + +void CConnman::DeleteNode(CNode* pnode) +{ + assert(pnode); + bool fUpdateConnectionTime = false; + GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime); + if(fUpdateConnectionTime) + addrman.Connected(pnode->addr); + delete pnode; +} + +CConnman::~CConnman() +{ +} + +size_t CConnman::GetAddressCount() const +{ + return addrman.size(); +} + +void CConnman::SetServices(const CService &addr, ServiceFlags nServices) +{ + addrman.SetServices(addr, nServices); +} + +void CConnman::MarkAddressGood(const CAddress& addr) +{ + addrman.Good(addr); +} + +void CConnman::AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty) +{ + addrman.Add(addr, addrFrom, nTimePenalty); +} + +void CConnman::AddNewAddresses(const std::vector& vAddr, const CAddress& addrFrom, int64_t nTimePenalty) +{ + addrman.Add(vAddr, addrFrom, nTimePenalty); +} + +std::vector CConnman::GetAddresses() +{ + return addrman.GetAddr(); +} + +bool CConnman::AddNode(const std::string& strNode) +{ + LOCK(cs_vAddedNodes); + for(std::vector::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { + if (strNode == *it) + return false; + } + + vAddedNodes.push_back(strNode); + return true; +} + +bool CConnman::RemoveAddedNode(const std::string& strNode) +{ + LOCK(cs_vAddedNodes); + for(std::vector::iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { + if (strNode == *it) { + vAddedNodes.erase(it); + return true; + } + } + return false; +} + +size_t CConnman::GetNodeCount(NumConnections flags) +{ + LOCK(cs_vNodes); + if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total + return vNodes.size(); + + int nNum = 0; + for(std::vector::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it) + if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + nNum++; + + return nNum; +} + +void CConnman::GetNodeStats(std::vector& vstats) +{ + vstats.clear(); + LOCK(cs_vNodes); + vstats.reserve(vNodes.size()); + for(std::vector::iterator it = vNodes.begin(); it != vNodes.end(); ++it) { + CNode* pnode = *it; + CNodeStats stats; + pnode->copyStats(stats); + vstats.push_back(stats); + } +} + +bool CConnman::DisconnectAddress(const CNetAddr& netAddr) +{ + if (CNode* pnode = FindNode(netAddr)) { + pnode->fDisconnect = true; + return true; + } + return false; +} + +bool CConnman::DisconnectSubnet(const CSubNet& subNet) +{ + if (CNode* pnode = FindNode(subNet)) { + pnode->fDisconnect = true; + return true; + } + return false; +} + +bool CConnman::DisconnectNode(const std::string& strNode) +{ + if (CNode* pnode = FindNode(strNode)) { + pnode->fDisconnect = true; + return true; + } + return false; +} +bool CConnman::DisconnectNode(NodeId id) +{ + LOCK(cs_vNodes); + for(CNode* pnode : vNodes) { + if (id == pnode->id) { + pnode->fDisconnect = true; + return true; + } + } + return false; +} + +void CConnman::RelayTransaction(const CTransaction& tx) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(10000); @@ -2273,7 +2411,7 @@ void RelayTransaction(const CTransaction& tx) RelayTransaction(tx, ss); } -void RelayTransaction(const CTransaction& tx, const CDataStream& ss) +void CConnman::RelayTransaction(const CTransaction& tx, const CDataStream& ss) { uint256 hash = tx.GetHash(); int nInv = static_cast(CPrivateSend::GetDSTX(hash)) ? MSG_DSTX : @@ -2307,20 +2445,20 @@ void RelayTransaction(const CTransaction& tx, const CDataStream& ss) } } -void RelayInv(CInv &inv, const int minProtoVersion) { +void CConnman::RelayInv(CInv &inv, const int minProtoVersion) { LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) if(pnode->nVersion >= minProtoVersion) pnode->PushInventory(inv); } -void CNode::RecordBytesRecv(uint64_t bytes) +void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); nTotalBytesRecv += bytes; } -void CNode::RecordBytesSent(uint64_t bytes) +void CConnman::RecordBytesSent(uint64_t bytes) { LOCK(cs_totalBytesSent); nTotalBytesSent += bytes; @@ -2337,7 +2475,7 @@ void CNode::RecordBytesSent(uint64_t bytes) nMaxOutboundTotalBytesSentInCycle += bytes; } -void CNode::SetMaxOutboundTarget(uint64_t limit) +void CConnman::SetMaxOutboundTarget(uint64_t limit) { LOCK(cs_totalBytesSent); uint64_t recommendedMinimum = (nMaxOutboundTimeframe / 600) * MAX_BLOCK_SIZE; @@ -2347,19 +2485,19 @@ void CNode::SetMaxOutboundTarget(uint64_t limit) LogPrintf("Max outbound target is very small (%s bytes) and will be overshot. Recommended minimum is %s bytes.\n", nMaxOutboundLimit, recommendedMinimum); } -uint64_t CNode::GetMaxOutboundTarget() +uint64_t CConnman::GetMaxOutboundTarget() { LOCK(cs_totalBytesSent); return nMaxOutboundLimit; } -uint64_t CNode::GetMaxOutboundTimeframe() +uint64_t CConnman::GetMaxOutboundTimeframe() { LOCK(cs_totalBytesSent); return nMaxOutboundTimeframe; } -uint64_t CNode::GetMaxOutboundTimeLeftInCycle() +uint64_t CConnman::GetMaxOutboundTimeLeftInCycle() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2373,7 +2511,7 @@ uint64_t CNode::GetMaxOutboundTimeLeftInCycle() return (cycleEndTime < now) ? 0 : cycleEndTime - GetTime(); } -void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) +void CConnman::SetMaxOutboundTimeframe(uint64_t timeframe) { LOCK(cs_totalBytesSent); if (nMaxOutboundTimeframe != timeframe) @@ -2385,7 +2523,7 @@ void CNode::SetMaxOutboundTimeframe(uint64_t timeframe) nMaxOutboundTimeframe = timeframe; } -bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) +bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2405,7 +2543,7 @@ bool CNode::OutboundTargetReached(bool historicalBlockServingLimit) return false; } -uint64_t CNode::GetOutboundTargetBytesLeft() +uint64_t CConnman::GetOutboundTargetBytesLeft() { LOCK(cs_totalBytesSent); if (nMaxOutboundLimit == 0) @@ -2414,18 +2552,33 @@ uint64_t CNode::GetOutboundTargetBytesLeft() return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle; } -uint64_t CNode::GetTotalBytesRecv() +uint64_t CConnman::GetTotalBytesRecv() { LOCK(cs_totalBytesRecv); return nTotalBytesRecv; } -uint64_t CNode::GetTotalBytesSent() +uint64_t CConnman::GetTotalBytesSent() { LOCK(cs_totalBytesSent); return nTotalBytesSent; } +ServiceFlags CConnman::GetLocalServices() const +{ + return nLocalServices; +} + +void CConnman::SetBestHeight(int height) +{ + nBestHeight.store(height, std::memory_order_release); +} + +int CConnman::GetBestHeight() const +{ + return nBestHeight.load(std::memory_order_acquire); +} + void CNode::Fuzz(int nChance) { if (!fSuccessfullyConnected) return; // Don't fuzz initial handshake @@ -2461,118 +2614,10 @@ void CNode::Fuzz(int nChance) Fuzz(2); } -// -// CAddrDB -// +unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; } +unsigned int CConnman::GetSendBufferSize() const{ return nSendBufferMaxSize; } -CAddrDB::CAddrDB() -{ - pathAddr = GetDataDir() / "peers.dat"; -} - -bool CAddrDB::Write(const CAddrMan& addr) -{ - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("peers.dat.%04x", randv); - - // serialize addresses, checksum data up to that point, then append csum - CDataStream ssPeers(SER_DISK, CLIENT_VERSION); - ssPeers << FLATDATA(Params().MessageStart()); - ssPeers << addr; - uint256 hash = Hash(ssPeers.begin(), ssPeers.end()); - ssPeers << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); - - // Write and commit header, data - try { - fileout << ssPeers; - } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); - - // replace existing peers.dat, if any, with new peers.dat.XXXX - if (!RenameOver(pathTmp, pathAddr)) - return error("%s: Rename-into-place failed", __func__); - - return true; -} - -bool CAddrDB::Read(CAddrMan& addr) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathAddr.string().c_str(), "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathAddr.string()); - - // use file size to size memory buffer - uint64_t fileSize = boost::filesystem::file_size(pathAddr); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssPeers(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssPeers.begin(), ssPeers.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - return Read(addr, ssPeers); -} - -bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers) -{ - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssPeers >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssPeers >> addr; - } - catch (const std::exception& e) { - // de-serialization has failed, ensure addrman is left in a clean state - addr.Clear(); - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - return true; -} - -unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); } -unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); } - -CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn, bool fNetworkNodeIn) : +CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNameIn, bool fInboundIn, bool fNetworkNodeIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), addrKnown(5000, 0.001), filterInventoryKnown(50000, 0.000001) @@ -2622,15 +2667,17 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa fMasternode = false; nMinPingUsecTime = std::numeric_limits::max(); vchKeyedNetGroup = CalculateKeyedNetGroup(addr); + id = idIn; + nOptimisticBytesWritten = 0; + nLocalServices = nLocalServicesIn; + + GetRandBytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); + nMyStartingHeight = nMyStartingHeightIn; + BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes()) mapRecvBytesPerMsgCmd[msg] = 0; mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; - { - LOCK(cs_nLastNodeId); - id = nLastNodeId++; - } - if(fNetworkNode || fInbound) AddRef(); @@ -2642,8 +2689,6 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa // Be shy and don't send version until we hear if (hSocket != INVALID_SOCKET && !fInbound) PushVersion(); - - GetNodeSignals().InitializeNode(GetId(), this); } CNode::~CNode() @@ -2652,8 +2697,6 @@ CNode::~CNode() if (pfilter) delete pfilter; - - GetNodeSignals().FinalizeNode(GetId()); } void CNode::AskFor(const CInv& inv) @@ -2760,7 +2803,7 @@ void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) // If write queue empty, attempt "optimistic write" if (it == vSendMsg.begin()) - SocketSendData(this); + nOptimisticBytesWritten += SocketSendData(this); LEAVE_CRITICAL_SECTION(cs_vSend); } @@ -2786,112 +2829,37 @@ std::vector CNode::CalculateKeyedNetGroup(CAddress& address) return vch; } -// -// CBanDB -// - -CBanDB::CBanDB() +bool CConnman::ForNode(const CService& addr, std::function func) { - pathBanlist = GetDataDir() / "banlist.dat"; + CNode* found = nullptr; + LOCK(cs_vNodes); + for (auto&& pnode : vNodes) { + if((CService)pnode->addr == addr) { + found = pnode; + break; + } + } + return found != nullptr && func(found); } -bool CBanDB::Write(const banmap_t& banSet) +bool CConnman::ForNode(NodeId id, std::function func) { - // Generate random temporary filename - unsigned short randv = 0; - GetRandBytes((unsigned char*)&randv, sizeof(randv)); - std::string tmpfn = strprintf("banlist.dat.%04x", randv); - - // serialize banlist, checksum data up to that point, then append csum - CDataStream ssBanlist(SER_DISK, CLIENT_VERSION); - ssBanlist << FLATDATA(Params().MessageStart()); - ssBanlist << banSet; - uint256 hash = Hash(ssBanlist.begin(), ssBanlist.end()); - ssBanlist << hash; - - // open temp output file, and associate with CAutoFile - boost::filesystem::path pathTmp = GetDataDir() / tmpfn; - FILE *file = fopen(pathTmp.string().c_str(), "wb"); - CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) - return error("%s: Failed to open file %s", __func__, pathTmp.string()); - - // Write and commit header, data - try { - fileout << ssBanlist; + CNode* found = nullptr; + LOCK(cs_vNodes); + for (auto&& pnode : vNodes) { + if(pnode->id == id) { + found = pnode; + break; + } } - catch (const std::exception& e) { - return error("%s: Serialize or I/O error - %s", __func__, e.what()); - } - FileCommit(fileout.Get()); - fileout.fclose(); - - // replace existing banlist.dat, if any, with new banlist.dat.XXXX - if (!RenameOver(pathTmp, pathBanlist)) - return error("%s: Rename-into-place failed", __func__); - - return true; -} - -bool CBanDB::Read(banmap_t& banSet) -{ - // open input file, and associate with CAutoFile - FILE *file = fopen(pathBanlist.string().c_str(), "rb"); - CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("%s: Failed to open file %s", __func__, pathBanlist.string()); - - // use file size to size memory buffer - uint64_t fileSize = boost::filesystem::file_size(pathBanlist); - uint64_t dataSize = 0; - // Don't try to resize to a negative number if file is small - if (fileSize >= sizeof(uint256)) - dataSize = fileSize - sizeof(uint256); - std::vector vchData; - vchData.resize(dataSize); - uint256 hashIn; - - // read data and checksum from file - try { - filein.read((char *)&vchData[0], dataSize); - filein >> hashIn; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - filein.fclose(); - - CDataStream ssBanlist(vchData, SER_DISK, CLIENT_VERSION); - - // verify stored checksum matches input data - uint256 hashTmp = Hash(ssBanlist.begin(), ssBanlist.end()); - if (hashIn != hashTmp) - return error("%s: Checksum mismatch, data corrupted", __func__); - - unsigned char pchMsgTmp[4]; - try { - // de-serialize file header (network specific magic number) and .. - ssBanlist >> FLATDATA(pchMsgTmp); - - // ... verify the network matches ours - if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp))) - return error("%s: Invalid network magic number", __func__); - - // de-serialize address data into one CAddrMan object - ssBanlist >> banSet; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - return true; + return found != nullptr && func(found); } int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds) { return nNow + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5); } -std::vector CopyNodeVector() +std::vector CConnman::CopyNodeVector() { std::vector vecNodesCopy; LOCK(cs_vNodes); @@ -2903,8 +2871,9 @@ std::vector CopyNodeVector() return vecNodesCopy; } -void ReleaseNodeVector(const std::vector& vecNodes) +void CConnman::ReleaseNodeVector(const std::vector& vecNodes) { + LOCK(cs_vNodes); for(size_t i = 0; i < vecNodes.size(); ++i) { CNode* pnode = vecNodes[i]; pnode->Release(); diff --git a/src/net.h b/src/net.h index 8f1da5631..e63381cbc 100644 --- a/src/net.h +++ b/src/net.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_NET_H #define BITCOIN_NET_H +#include "addrdb.h" +#include "addrman.h" #include "bloom.h" #include "compat.h" #include "limitedmap.h" @@ -20,6 +22,7 @@ #include #include #include +#include #ifndef WIN32 #include @@ -53,6 +56,10 @@ static const unsigned int MAX_ADDR_TO_SEND = 1000; static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024; /** Maximum length of strSubVer in `version` message */ static const unsigned int MAX_SUBVERSION_LENGTH = 256; +/** Maximum number of outgoing nodes */ +static const int MAX_OUTBOUND_CONNECTIONS = 8; +/** Maximum number if outgoing masternodes */ +static const int MAX_OUTBOUND_MASTERNODE_CONNECTIONS = 20; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ @@ -81,27 +88,330 @@ static const ServiceFlags REQUIRED_SERVICES = NODE_NETWORK; // NOTE: When adjusting this, update rpcnet:setban's help ("24h") static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban -unsigned int ReceiveFloodSize(); -unsigned int SendBufferSize(); +typedef int NodeId; -void AddOneShot(const std::string& strDest); -void AddressCurrentlyConnected(const CService& addr); -CNode* FindNode(const CNetAddr& ip); -CNode* FindNode(const CSubNet& subNet); -CNode* FindNode(const std::string& addrName); -CNode* FindNode(const CService& ip); -// fConnectToMasternode should be 'true' only if you want this node to allow to connect to itself -// and/or you want it to be disconnected on CMasternodeMan::ProcessMasternodeConnections() -CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL, bool fConnectToMasternode = false); -bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false); +struct AddedNodeInfo +{ + std::string strAddedNode; + CService resolvedAddress; + bool fConnected; + bool fInbound; +}; + +class CTransaction; +class CNodeStats; +class CClientUIInterface; + +class CConnman +{ +public: + + enum NumConnections { + CONNECTIONS_NONE = 0, + CONNECTIONS_IN = (1U << 0), + CONNECTIONS_OUT = (1U << 1), + CONNECTIONS_ALL = (CONNECTIONS_IN | CONNECTIONS_OUT), + }; + + struct Options + { + ServiceFlags nLocalServices = NODE_NONE; + ServiceFlags nRelevantServices = NODE_NONE; + int nMaxConnections = 0; + int nMaxOutbound = 0; + int nMaxFeeler = 0; + int nBestHeight = 0; + CClientUIInterface* uiInterface = nullptr; + unsigned int nSendBufferMaxSize = 0; + unsigned int nReceiveFloodSize = 0; + }; + CConnman(); + ~CConnman(); + bool Start(boost::thread_group& threadGroup, CScheduler& scheduler, std::string& strNodeError, Options options); + void Stop(); + bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); + bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false, bool fFeeler = false); + bool CheckIncomingNonce(uint64_t nonce); + + // fConnectToMasternode should be 'true' only if you want this node to allow to connect to itself + // and/or you want it to be disconnected on CMasternodeMan::ProcessMasternodeConnections() + // Unfortunately, can't make this method private like in Bitcoin, + // because it's used in many Dash-specific places (masternode, privatesend). + CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL, bool fConnectToMasternode = false); + + bool ForNode(NodeId id, std::function func); + bool ForNode(const CService& addr, std::function func); + + template + bool ForEachNodeContinueIf(Callable&& func) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) + if(!func(node)) + return false; + return true; + }; + + template + bool ForEachNodeContinueIf(Callable&& func) const + { + LOCK(cs_vNodes); + for (const auto& node : vNodes) + if(!func(node)) + return false; + return true; + }; + + template + bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) + { + bool ret = true; + LOCK(cs_vNodes); + for (auto&& node : vNodes) + if(!pre(node)) { + ret = false; + break; + } + post(); + return ret; + }; + + template + bool ForEachNodeContinueIfThen(Callable&& pre, CallableAfter&& post) const + { + bool ret = true; + LOCK(cs_vNodes); + for (const auto& node : vNodes) + if(!pre(node)) { + ret = false; + break; + } + post(); + return ret; + }; + + template + void ForEachNode(Callable&& func) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) + func(node); + }; + + template + void ForEachNode(Callable&& func) const + { + LOCK(cs_vNodes); + for (const auto& node : vNodes) + func(node); + }; + + template + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) + { + LOCK(cs_vNodes); + for (auto&& node : vNodes) + pre(node); + post(); + }; + + template + void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const + { + LOCK(cs_vNodes); + for (const auto& node : vNodes) + pre(node); + post(); + }; + + std::vector CopyNodeVector(); + void ReleaseNodeVector(const std::vector& vecNodes); + + void RelayTransaction(const CTransaction& tx); + void RelayTransaction(const CTransaction& tx, const CDataStream& ss); + void RelayInv(CInv &inv, const int minProtoVersion = MIN_PEER_PROTO_VERSION); + + // Addrman functions + size_t GetAddressCount() const; + void SetServices(const CService &addr, ServiceFlags nServices); + void MarkAddressGood(const CAddress& addr); + void AddNewAddress(const CAddress& addr, const CAddress& addrFrom, int64_t nTimePenalty = 0); + void AddNewAddresses(const std::vector& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0); + std::vector GetAddresses(); + void AddressCurrentlyConnected(const CService& addr); + + // Denial-of-service detection/prevention + // The idea is to detect peers that are behaving + // badly and disconnect/ban them, but do it in a + // one-coding-mistake-won't-shatter-the-entire-network + // way. + // IMPORTANT: There should be nothing I can give a + // node that it will forward on that will make that + // node's peers drop it. If there is, an attacker + // can isolate a node and/or try to split the network. + // Dropping a node for sending stuff that is invalid + // now but might be valid in a later version is also + // dangerous, because it can cause a network split + // between nodes running old code and nodes running + // new code. + void Ban(const CNetAddr& netAddr, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void Ban(const CSubNet& subNet, const BanReason& reason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); + void ClearBanned(); // needed for unit testing + bool IsBanned(CNetAddr ip); + bool IsBanned(CSubNet subnet); + bool Unban(const CNetAddr &ip); + bool Unban(const CSubNet &ip); + void GetBanned(banmap_t &banmap); + void SetBanned(const banmap_t &banmap); + + void AddOneShot(const std::string& strDest); + + bool AddNode(const std::string& node); + bool RemoveAddedNode(const std::string& node); + std::vector GetAddedNodeInfo(); + + size_t GetNodeCount(NumConnections num); + void GetNodeStats(std::vector& vstats); + bool DisconnectAddress(const CNetAddr& addr); + bool DisconnectNode(const std::string& node); + bool DisconnectNode(NodeId id); + bool DisconnectSubnet(const CSubNet& subnet); + + unsigned int GetSendBufferSize() const; + + void AddWhitelistedRange(const CSubNet &subnet); + + ServiceFlags GetLocalServices() const; + + //!set the max outbound target in bytes + void SetMaxOutboundTarget(uint64_t limit); + uint64_t GetMaxOutboundTarget(); + + //!set the timeframe for the max outbound target + void SetMaxOutboundTimeframe(uint64_t timeframe); + uint64_t GetMaxOutboundTimeframe(); + + //!check if the outbound target is reached + // if param historicalBlockServingLimit is set true, the function will + // response true if the limit for serving historical blocks has been reached + bool OutboundTargetReached(bool historicalBlockServingLimit); + + //!response the bytes left in the current max outbound cycle + // in case of no limit, it will always response 0 + uint64_t GetOutboundTargetBytesLeft(); + + //!response the time in second left in the current max outbound cycle + // in case of no limit, it will always response 0 + uint64_t GetMaxOutboundTimeLeftInCycle(); + + uint64_t GetTotalBytesRecv(); + uint64_t GetTotalBytesSent(); + + void SetBestHeight(int height); + int GetBestHeight() const; + + +private: + struct ListenSocket { + SOCKET socket; + bool whitelisted; + + ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {} + }; + + void ThreadOpenAddedConnections(); + void ProcessOneShot(); + void ThreadOpenConnections(); + void ThreadMessageHandler(); + void AcceptConnection(const ListenSocket& hListenSocket); + void ThreadSocketHandler(); + void ThreadDNSAddressSeed(); + void ThreadMnbRequestConnections(); + + CNode* FindNode(const CNetAddr& ip); + CNode* FindNode(const CSubNet& subNet); + CNode* FindNode(const std::string& addrName); + CNode* FindNode(const CService& addr); + + bool AttemptToEvictConnection(); + bool IsWhitelistedRange(const CNetAddr &addr); + + void DeleteNode(CNode* pnode); + + NodeId GetNewNodeId(); + + //!check is the banlist has unwritten changes + bool BannedSetIsDirty(); + //!set the "dirty" flag for the banlist + void SetBannedSetDirty(bool dirty=true); + //!clean unused entries (if bantime has expired) + void SweepBanned(); + void DumpAddresses(); + void DumpData(); + void DumpBanlist(); + + unsigned int GetReceiveFloodSize() const; + + // Network stats + void RecordBytesRecv(uint64_t bytes); + void RecordBytesSent(uint64_t bytes); + + // Network usage totals + CCriticalSection cs_totalBytesRecv; + CCriticalSection cs_totalBytesSent; + uint64_t nTotalBytesRecv; + uint64_t nTotalBytesSent; + + // outbound limit & stats + uint64_t nMaxOutboundTotalBytesSentInCycle; + uint64_t nMaxOutboundCycleStartTime; + uint64_t nMaxOutboundLimit; + uint64_t nMaxOutboundTimeframe; + + // Whitelisted ranges. Any node connecting from these is automatically + // whitelisted (as well as those connecting to whitelisted binds). + std::vector vWhitelistedRange; + CCriticalSection cs_vWhitelistedRange; + + unsigned int nSendBufferMaxSize; + unsigned int nReceiveFloodSize; + + std::vector vhListenSocket; + banmap_t setBanned; + CCriticalSection cs_setBanned; + bool setBannedIsDirty; + bool fAddressesInitialized; + CAddrMan addrman; + std::deque vOneShots; + CCriticalSection cs_vOneShots; + std::vector vAddedNodes; + CCriticalSection cs_vAddedNodes; + std::vector vNodes; + std::list vNodesDisconnected; + mutable CCriticalSection cs_vNodes; + std::atomic nLastNodeId; + boost::condition_variable messageHandlerCondition; + + /** Services this instance offers */ + ServiceFlags nLocalServices; + + /** Services this instance cares about */ + ServiceFlags nRelevantServices; + + CSemaphore *semOutbound; + CSemaphore *semMasternodeOutbound; + int nMaxConnections; + int nMaxOutbound; + int nMaxFeeler; + std::atomic nBestHeight; + CClientUIInterface* clientInterface; +}; +extern std::unique_ptr g_connman; +void Discover(boost::thread_group& threadGroup); void MapPort(bool fUseUPnP); unsigned short GetListenPort(); bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false); -void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler); -bool StopNode(); -void SocketSendData(CNode *pnode); - -typedef int NodeId; +size_t SocketSendData(CNode *pnode); struct CombinerAll { @@ -121,11 +431,10 @@ struct CombinerAll // Signals for message handling struct CNodeSignals { - boost::signals2::signal GetHeight; - boost::signals2::signal ProcessMessages; - boost::signals2::signal SendMessages; + boost::signals2::signal ProcessMessages; + boost::signals2::signal SendMessages; boost::signals2::signal InitializeNode; - boost::signals2::signal FinalizeNode; + boost::signals2::signal FinalizeNode; }; @@ -156,31 +465,17 @@ bool IsLocal(const CService& addr); bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL); bool IsReachable(enum Network net); bool IsReachable(const CNetAddr &addr); -CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL); +CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices); extern bool fDiscover; extern bool fListen; -extern ServiceFlags nLocalServices; -extern uint64_t nLocalHostNonce; -extern CAddrMan addrman; -/** Maximum number of connections to simultaneously allow (aka connection slots) */ -extern int nMaxConnections; - -extern std::vector vNodes; -extern CCriticalSection cs_vNodes; extern std::map mapRelay; extern std::deque > vRelayExpiration; extern CCriticalSection cs_mapRelay; extern limitedmap mapAlreadyAskedFor; -extern std::vector vAddedNodes; -extern CCriticalSection cs_vAddedNodes; - -extern NodeId nLastNodeId; -extern CCriticalSection cs_nLastNodeId; - /** Subversion as sent to the P2P network in `version` messages */ extern std::string strSubVersion; @@ -261,67 +556,6 @@ public: }; -typedef enum BanReason -{ - BanReasonUnknown = 0, - BanReasonNodeMisbehaving = 1, - BanReasonManuallyAdded = 2 -} BanReason; - -class CBanEntry -{ -public: - static const int CURRENT_VERSION=1; - int nVersion; - int64_t nCreateTime; - int64_t nBanUntil; - uint8_t banReason; - - CBanEntry() - { - SetNull(); - } - - CBanEntry(int64_t nCreateTimeIn) - { - SetNull(); - nCreateTime = nCreateTimeIn; - } - - ADD_SERIALIZE_METHODS; - - template - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(this->nVersion); - nVersion = this->nVersion; - READWRITE(nCreateTime); - READWRITE(nBanUntil); - READWRITE(banReason); - } - - void SetNull() - { - nVersion = CBanEntry::CURRENT_VERSION; - nCreateTime = 0; - nBanUntil = 0; - banReason = BanReasonUnknown; - } - - std::string banReasonToString() - { - switch (banReason) { - case BanReasonNodeMisbehaving: - return "node misbehaving"; - case BanReasonManuallyAdded: - return "manually added"; - default: - return "unknown"; - } - } -}; - -typedef std::map banmap_t; - /** Information about a peer */ class CNode { @@ -333,6 +567,7 @@ public: CDataStream ssSend; size_t nSendSize; // total size of all vSendMsg entries size_t nSendOffset; // offset inside the first vSendMsg already sent + uint64_t nOptimisticBytesWritten; uint64_t nSendBytes; std::deque vSendMsg; CCriticalSection cs_vSend; @@ -381,17 +616,6 @@ public: NodeId id; protected: - // Denial-of-service detection/prevention - // Key is IP address, value is banned-until-time - static banmap_t setBanned; - static CCriticalSection cs_setBanned; - static bool setBannedIsDirty; - - // Whitelisted ranges. Any node connecting from these is automatically - // whitelisted (as well as those connecting to whitelisted binds). - static std::vector vWhitelistedRange; - static CCriticalSection cs_vWhitelistedRange; - mapMsgCmdSize mapSendBytesPerMsgCmd; mapMsgCmdSize mapRecvBytesPerMsgCmd; @@ -439,22 +663,10 @@ public: std::vector vchKeyedNetGroup; - CNode(SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool fNetworkNodeIn = false); + CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, const std::string &addrNameIn = "", bool fInboundIn = false, bool fNetworkNodeIn = false); ~CNode(); private: - // Network usage totals - static CCriticalSection cs_totalBytesRecv; - static CCriticalSection cs_totalBytesSent; - static uint64_t nTotalBytesRecv; - static uint64_t nTotalBytesSent; - - // outbound limit & stats - static uint64_t nMaxOutboundTotalBytesSentInCycle; - static uint64_t nMaxOutboundCycleStartTime; - static uint64_t nMaxOutboundLimit; - static uint64_t nMaxOutboundTimeframe; - // Secret key for computing keyed net groups static std::vector vchSecretKey; @@ -463,12 +675,19 @@ private: CNode(const CNode&); void operator=(const CNode&); + uint64_t nLocalHostNonce; + ServiceFlags nLocalServices; + int nMyStartingHeight; public: NodeId GetId() const { return id; } + uint64_t GetLocalNonce() const { + return nLocalHostNonce; + } + int GetRefCount() { LOCK(cs_nRefCount); @@ -486,7 +705,7 @@ public: } // requires LOCK(cs_vRecvMsg) - bool ReceiveMsgBytes(const char *pch, unsigned int nBytes); + bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete); // requires LOCK(cs_vRecvMsg) void SetRecvVersion(int nVersionIn) @@ -781,69 +1000,12 @@ public: void CloseSocketDisconnect(); - // Denial-of-service detection/prevention - // The idea is to detect peers that are behaving - // badly and disconnect/ban them, but do it in a - // one-coding-mistake-won't-shatter-the-entire-network - // way. - // IMPORTANT: There should be nothing I can give a - // node that it will forward on that will make that - // node's peers drop it. If there is, an attacker - // can isolate a node and/or try to split the network. - // Dropping a node for sending stuff that is invalid - // now but might be valid in a later version is also - // dangerous, because it can cause a network split - // between nodes running old code and nodes running - // new code. - static void ClearBanned(); // needed for unit testing - static bool IsBanned(CNetAddr ip); - static bool IsBanned(CSubNet subnet); - static void Ban(const CNetAddr &ip, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); - static void Ban(const CSubNet &subNet, const BanReason &banReason, int64_t bantimeoffset = 0, bool sinceUnixEpoch = false); - static bool Unban(const CNetAddr &ip); - static bool Unban(const CSubNet &ip); - static void GetBanned(banmap_t &banmap); - static void SetBanned(const banmap_t &banmap); - - //!check is the banlist has unwritten changes - static bool BannedSetIsDirty(); - //!set the "dirty" flag for the banlist - static void SetBannedSetDirty(bool dirty=true); - //!clean unused entries (if bantime has expired) - static void SweepBanned(); - 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); - - static uint64_t GetTotalBytesRecv(); - static uint64_t GetTotalBytesSent(); - - //!set the max outbound target in bytes - static void SetMaxOutboundTarget(uint64_t limit); - static uint64_t GetMaxOutboundTarget(); - - //!set the timeframe for the max outbound target - static void SetMaxOutboundTimeframe(uint64_t timeframe); - static uint64_t GetMaxOutboundTimeframe(); - - //!check if the outbound target is reached - // if param historicalBlockServingLimit is set true, the function will - // response true if the limit for serving historical blocks has been reached - static bool OutboundTargetReached(bool historicalBlockServingLimit); - - //!response the bytes left in the current max outbound cycle - // in case of no limit, it will always response 0 - static uint64_t GetOutboundTargetBytesLeft(); - - //!response the time in second left in the current max outbound cycle - // in case of no limit, it will always response 0 - static uint64_t GetMaxOutboundTimeLeftInCycle(); + ServiceFlags GetLocalServices() const + { + return nLocalServices; + } static std::vector CalculateKeyedNetGroup(CAddress& address); }; @@ -854,49 +1016,9 @@ public: static void callCleanup(); }; -class CTransaction; -void RelayTransaction(const CTransaction& tx); -void RelayTransaction(const CTransaction& tx, const CDataStream& ss); -void RelayInv(CInv &inv, const int minProtoVersion = MIN_PEER_PROTO_VERSION); -/** Access to the (IP) address database (peers.dat) */ -class CAddrDB -{ -private: - boost::filesystem::path pathAddr; -public: - CAddrDB(); - bool Write(const CAddrMan& addr); - bool Read(CAddrMan& addr); - bool Read(CAddrMan& addr, CDataStream& ssPeers); -}; - -/** Access to the banlist database (banlist.dat) */ -class CBanDB -{ -private: - boost::filesystem::path pathBanlist; -public: - CBanDB(); - bool Write(const banmap_t& banSet); - bool Read(banmap_t& banSet); -}; /** Return a timestamp in the future (in microseconds) for exponentially distributed events. */ int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds); -std::vector CopyNodeVector(); - -void ReleaseNodeVector(const std::vector& vecNodes); - -struct AddedNodeInfo -{ - std::string strAddedNode; - CService resolvedAddress; - bool fConnected; - bool fInbound; -}; - -std::vector GetAddedNodeInfo(); - #endif // BITCOIN_NET_H diff --git a/src/privatesend-client.cpp b/src/privatesend-client.cpp index 469bb410f..76a1cabb6 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -656,7 +656,7 @@ bool CPrivateSendClient::CheckAutomaticBackup() // // Passively run mixing in the background to anonymize funds based on the given configuration. // -bool CPrivateSendClient::DoAutomaticDenominating(bool fDryRun) +bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun) { if(!fEnablePrivateSend || fMasterNode || !pCurrentBlockIndex) return false; if(!pwalletMain || pwalletMain->IsLocked(true)) return false; @@ -737,11 +737,11 @@ bool CPrivateSendClient::DoAutomaticDenominating(bool fDryRun) // there are funds to denominate and denominated balance does not exceed // max amount to mix yet. if(nBalanceAnonimizableNonDenom >= nValueMin + CPrivateSend::GetCollateralAmount() && nBalanceDenominated < nPrivateSendAmount*COIN) - return CreateDenominated(); + return CreateDenominated(connman); //check if we have the collateral sized inputs if(!pwalletMain->HasCollateralInputs()) - return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(); + return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(connman); if(nSessionID) { strAutoDenomResult = _("Mixing in progress..."); @@ -849,21 +849,23 @@ bool CPrivateSendClient::JoinExistingQueue(CAmount nBalanceNeedsAnonymized) vecMasternodesUsed.push_back(dsq.vin); CNode* pnodeFound = NULL; - { - LOCK(cs_vNodes); - pnodeFound = FindNode(infoMn.addr); - if(pnodeFound) { - if(pnodeFound->fDisconnect) { - continue; - } else { - pnodeFound->AddRef(); - } + bool fDisconnect = false; + g_connman->ForNode(infoMn.addr, [&pnodeFound, &fDisconnect](CNode* pnode) { + pnodeFound = pnode; + if(pnodeFound->fDisconnect) { + fDisconnect = true; + } else { + pnodeFound->AddRef(); } - } + return true; + }); + if (fDisconnect) + continue; LogPrintf("CPrivateSendClient::JoinExistingQueue -- attempt to connect to masternode from queue, addr=%s\n", infoMn.addr.ToString()); // connect to Masternode and submit the queue request - CNode* pnode = (pnodeFound && pnodeFound->fMasternode) ? pnodeFound : ConnectNode(CAddress(infoMn.addr, NODE_NETWORK), NULL, true); + // TODO: Pass CConnman instance somehow and don't use global variable. + CNode* pnode = (pnodeFound && pnodeFound->fMasternode) ? pnodeFound : g_connman->ConnectNode(CAddress(infoMn.addr, NODE_NETWORK), NULL, true); if(pnode) { infoMixingMasternode = infoMn; nSessionDenom = dsq.nDenom; @@ -922,21 +924,24 @@ bool CPrivateSendClient::StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsA } CNode* pnodeFound = NULL; - { - LOCK(cs_vNodes); - pnodeFound = FindNode(infoMn.addr); - if(pnodeFound) { - if(pnodeFound->fDisconnect) { - nTries++; - continue; - } else { - pnodeFound->AddRef(); - } + bool fDisconnect = false; + g_connman->ForNode(infoMn.addr, [&pnodeFound, &fDisconnect](CNode* pnode) { + pnodeFound = pnode; + if(pnodeFound->fDisconnect) { + fDisconnect = true; + } else { + pnodeFound->AddRef(); } + return true; + }); + if (fDisconnect) { + nTries++; + continue; } LogPrintf("CPrivateSendClient::StartNewQueue -- attempt %d connection to Masternode %s\n", nTries, infoMn.addr.ToString()); - CNode* pnode = (pnodeFound && pnodeFound->fMasternode) ? pnodeFound : ConnectNode(CAddress(infoMn.addr, NODE_NETWORK), NULL, true); + // TODO: Pass CConnman instance somehow and don't use global variable. + CNode* pnode = (pnodeFound && pnodeFound->fMasternode) ? pnodeFound : g_connman->ConnectNode(CAddress(infoMn.addr, NODE_NETWORK), NULL, true); if(pnode) { LogPrintf("CPrivateSendClient::StartNewQueue -- connected, addr=%s\n", infoMn.addr.ToString()); infoMixingMasternode = infoMn; @@ -1121,7 +1126,7 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std:: } // Create collaterals by looping through inputs grouped by addresses -bool CPrivateSendClient::MakeCollateralAmounts() +bool CPrivateSendClient::MakeCollateralAmounts(CConnman& connman) { std::vector vecTally; if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally, false)) { @@ -1131,13 +1136,13 @@ bool CPrivateSendClient::MakeCollateralAmounts() // First try to use only non-denominated funds BOOST_FOREACH(CompactTallyItem& item, vecTally) { - if(!MakeCollateralAmounts(item, false)) continue; + if(!MakeCollateralAmounts(item, false, connman)) continue; return true; } // There should be at least some denominated funds we should be able to break in pieces to continue mixing BOOST_FOREACH(CompactTallyItem& item, vecTally) { - if(!MakeCollateralAmounts(item, true)) continue; + if(!MakeCollateralAmounts(item, true, connman)) continue; return true; } @@ -1147,7 +1152,7 @@ bool CPrivateSendClient::MakeCollateralAmounts() } // Split up large inputs or create fee sized inputs -bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated) +bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman) { LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1204,7 +1209,7 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- txid=%s\n", wtx.GetHash().GetHex()); // use the same nCachedLastSuccessBlock as for DS mixinx to prevent race - if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + if(!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman)) { LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- CommitTransaction failed!\n"); return false; } @@ -1215,7 +1220,7 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem } // Create denominations by looping through inputs grouped by addresses -bool CPrivateSendClient::CreateDenominated() +bool CPrivateSendClient::CreateDenominated(CConnman& connman) { LOCK2(cs_main, pwalletMain->cs_wallet); @@ -1228,7 +1233,7 @@ bool CPrivateSendClient::CreateDenominated() bool fCreateMixingCollaterals = !pwalletMain->HasCollateralInputs(); BOOST_FOREACH(CompactTallyItem& item, vecTally) { - if(!CreateDenominated(item, fCreateMixingCollaterals)) continue; + if(!CreateDenominated(item, fCreateMixingCollaterals, connman)) continue; return true; } @@ -1237,7 +1242,7 @@ bool CPrivateSendClient::CreateDenominated() } // Create denominations -bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals) +bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman) { std::vector vecSend; CAmount nValueLeft = tallyItem.nAmount; @@ -1351,7 +1356,7 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo reservekeyCollateral.KeepKey(); LogPrintf("CPrivateSendClient::CreateDenominated -- %d keys keeped\n", reservekeyDenomVec.size() + 1); - if(!pwalletMain->CommitTransaction(wtx, reservekeyChange)) { + if(!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman)) { LogPrintf("CPrivateSendClient::CreateDenominated -- CommitTransaction failed!\n"); return false; } @@ -1367,12 +1372,11 @@ void CPrivateSendClient::RelayIn(const CDarkSendEntry& entry) { if(!infoMixingMasternode.fInfoValid) return; - LOCK(cs_vNodes); - CNode* pnode = FindNode(infoMixingMasternode.addr); - if(pnode != NULL) { + g_connman->ForNode(infoMixingMasternode.addr, [&entry](CNode* pnode) { LogPrintf("CPrivateSendClient::RelayIn -- found master, relaying message to %s\n", pnode->addr.ToString()); pnode->PushMessage(NetMsgType::DSVIN, entry); - } + return true; + }); } void CPrivateSendClient::SetState(PoolState nStateNew) @@ -1394,7 +1398,7 @@ void CPrivateSendClient::UpdatedBlockTip(const CBlockIndex *pindex) } //TODO: Rename/move to core -void ThreadCheckPrivateSendClient() +void ThreadCheckPrivateSendClient(CConnman& connman) { if(fLiteMode) return; // disable all Dash specific functionality @@ -1416,7 +1420,7 @@ void ThreadCheckPrivateSendClient() nTick++; privateSendClient.CheckTimeout(); if(nDoAutoNextRun == nTick) { - privateSendClient.DoAutomaticDenominating(); + privateSendClient.DoAutomaticDenominating(connman); nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN + GetRandInt(PRIVATESEND_AUTO_TIMEOUT_MAX - PRIVATESEND_AUTO_TIMEOUT_MIN); } } diff --git a/src/privatesend-client.h b/src/privatesend-client.h index abcba7a47..10a152e8b 100644 --- a/src/privatesend-client.h +++ b/src/privatesend-client.h @@ -10,6 +10,7 @@ #include "wallet/wallet.h" class CPrivateSendClient; +class CConnman; static const int DENOMS_COUNT_MAX = 100; @@ -66,12 +67,12 @@ private: bool StartNewQueue(CAmount nValueMin, CAmount nBalanceNeedsAnonymized); /// Create denominations - bool CreateDenominated(); - bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals); + bool CreateDenominated(CConnman& connman); + bool CreateDenominated(const CompactTallyItem& tallyItem, bool fCreateMixingCollaterals, CConnman& connman); /// Split up large inputs or make fee sized inputs - bool MakeCollateralAmounts(); - bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated); + bool MakeCollateralAmounts(CConnman& connman); + bool MakeCollateralAmounts(const CompactTallyItem& tallyItem, bool fTryDenominated, CConnman& connman); /// As a client, submit part of a future mixing transaction to a Masternode to start the process bool SubmitDenominate(); @@ -128,7 +129,7 @@ public: std::string GetStatus(); /// Passively run mixing in the background according to the configuration in settings - bool DoAutomaticDenominating(bool fDryRun=false); + bool DoAutomaticDenominating(CConnman& connman, bool fDryRun=false); void CheckTimeout(); @@ -138,6 +139,6 @@ public: void UpdatedBlockTip(const CBlockIndex *pindex); }; -void ThreadCheckPrivateSendClient(); +void ThreadCheckPrivateSendClient(CConnman& connman); #endif diff --git a/src/privatesend-server.cpp b/src/privatesend-server.cpp index 77e199430..18fd52fa7 100644 --- a/src/privatesend-server.cpp +++ b/src/privatesend-server.cpp @@ -367,7 +367,7 @@ void CPrivateSendServer::CommitFinalTransaction() LogPrintf("CPrivateSendServer::CommitFinalTransaction -- TRANSMITTING DSTX\n"); CInv inv(MSG_DSTX, hashTx); - RelayInv(inv); + g_connman->RelayInv(inv); // Tell the clients it was successful RelayCompletedTransaction(MSG_SUCCESS); @@ -452,7 +452,7 @@ void CPrivateSendServer::ChargeFees() // should never really happen LogPrintf("CPrivateSendServer::ChargeFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { - RelayTransaction(vecOffendersCollaterals[0]); + g_connman->RelayTransaction(vecOffendersCollaterals[0]); } } } @@ -487,7 +487,7 @@ void CPrivateSendServer::ChargeRandomFees() // should never really happen LogPrintf("CPrivateSendServer::ChargeRandomFees -- ERROR: AcceptToMemoryPool failed!\n"); } else { - RelayTransaction(txCollateral); + g_connman->RelayTransaction(txCollateral); } } } @@ -795,10 +795,10 @@ bool CPrivateSendServer::AddUserToExistingSession(int nDenom, CTransaction txCol void CPrivateSendServer::RelayFinalTransaction(const CTransaction& txFinal) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + g_connman->ForEachNode([&txFinal, this](CNode* pnode) { if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) pnode->PushMessage(NetMsgType::DSFINALTX, nSessionID, txFinal); + }); } void CPrivateSendServer::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) @@ -809,18 +809,18 @@ void CPrivateSendServer::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + g_connman->ForEachNode([nStatusUpdate, nMessageID, this](CNode* pnode) { if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) PushStatus(pnode, nStatusUpdate, nMessageID); + }); } void CPrivateSendServer::RelayCompletedTransaction(PoolMessage nMessageID) { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) + g_connman->ForEachNode([nMessageID, this](CNode* pnode) { if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) pnode->PushMessage(NetMsgType::DSCOMPLETE, nSessionID, (int)nMessageID); + }); } void CPrivateSendServer::SetState(PoolState nStateNew) diff --git a/src/privatesend.cpp b/src/privatesend.cpp index f02ffbd93..a4f195786 100644 --- a/src/privatesend.cpp +++ b/src/privatesend.cpp @@ -74,12 +74,12 @@ bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode) bool CDarksendQueue::Relay() { - std::vector vNodesCopy = CopyNodeVector(); + std::vector vNodesCopy = g_connman->CopyNodeVector(); BOOST_FOREACH(CNode* pnode, vNodesCopy) if(pnode->nVersion >= MIN_PRIVATESEND_PEER_PROTO_VERSION) pnode->PushMessage(NetMsgType::DSQUEUE, (*this)); - ReleaseNodeVector(vNodesCopy); + g_connman->ReleaseNodeVector(vNodesCopy); return true; } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 33792af5b..06bb23ba2 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -48,7 +48,8 @@ public: void refreshBanlist() { banmap_t banMap; - CNode::GetBanned(banMap); + if(g_connman) + g_connman->GetBanned(banMap); cachedBanlist.clear(); #if QT_VERSION >= 0x040700 diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 4d1d6f6f5..02f832e34 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -62,16 +62,18 @@ ClientModel::~ClientModel() int ClientModel::getNumConnections(unsigned int flags) const { - LOCK(cs_vNodes); - if (flags == CONNECTIONS_ALL) // Shortcut if we want total - return vNodes.size(); + CConnman::NumConnections connections = CConnman::CONNECTIONS_NONE; - int nNum = 0; - BOOST_FOREACH(const CNode* pnode, vNodes) - if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) - nNum++; + if(flags == CONNECTIONS_IN) + connections = CConnman::CONNECTIONS_IN; + else if (flags == CONNECTIONS_OUT) + connections = CConnman::CONNECTIONS_OUT; + else if (flags == CONNECTIONS_ALL) + connections = CConnman::CONNECTIONS_ALL; - return nNum; + if(g_connman) + return g_connman->GetNodeCount(connections); + return 0; } QString ClientModel::getMasternodeCountString() const @@ -94,12 +96,16 @@ int ClientModel::getNumBlocks() const quint64 ClientModel::getTotalBytesRecv() const { - return CNode::GetTotalBytesRecv(); + if(!g_connman) + return 0; + return g_connman->GetTotalBytesRecv(); } quint64 ClientModel::getTotalBytesSent() const { - return CNode::GetTotalBytesSent(); + if(!g_connman) + return 0; + return g_connman->GetTotalBytesSent(); } QDateTime ClientModel::getLastBlockDate() const diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 768614ad6..34bb90786 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -290,17 +290,17 @@ void copyEntryData(QAbstractItemView *view, int column, int role) } } -QString getEntryData(QAbstractItemView *view, int column, int role) +QVariant getEntryData(QAbstractItemView *view, int column, int role) { if(!view || !view->selectionModel()) - return QString(); + return QVariant(); QModelIndexList selection = view->selectionModel()->selectedRows(column); if(!selection.isEmpty()) { // Return first item - return (selection.at(0).data(role).toString()); + return (selection.at(0).data(role)); } - return QString(); + return QVariant(); } QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index a937f6c1a..29add470f 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -70,7 +70,7 @@ namespace GUIUtil @param[in] role Data role to extract from the model @see TransactionView::copyLabel, TransactionView::copyAmount, TransactionView::copyAddress */ - QString getEntryData(QAbstractItemView *view, int column, int role); + QVariant getEntryData(QAbstractItemView *view, int column, int role); void setClipboard(const QString& str); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index e09f23404..8aaa4fdf0 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -569,7 +569,7 @@ void OverviewPage::privateSendStatus() } void OverviewPage::privateSendAuto(){ - privateSendClient.DoAutomaticDenominating(); + privateSendClient.DoAutomaticDenominating(*g_connman); } void OverviewPage::privateSendReset(){ diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 5f7b3d97e..38727cfa6 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -24,6 +24,8 @@ bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombine switch(column) { + case PeerTableModel::NetNodeId: + return pLeft->nodeid < pRight->nodeid; case PeerTableModel::Address: return pLeft->addrName.compare(pRight->addrName) < 0; case PeerTableModel::Subversion: @@ -52,24 +54,21 @@ public: void refreshPeers() { { - TRY_LOCK(cs_vNodes, lockNodes); - if (!lockNodes) - { - // skip the refresh if we can't immediately get the lock - return; - } cachedNodeStats.clear(); + std::vector vstats; + if(g_connman) + g_connman->GetNodeStats(vstats); #if QT_VERSION >= 0x040700 - cachedNodeStats.reserve(vNodes.size()); + cachedNodeStats.reserve(vstats.size()); #endif - Q_FOREACH (CNode* pnode, vNodes) + Q_FOREACH (const CNodeStats& nodestats, vstats) { CNodeCombinedStats stats; stats.nodeStateStats.nMisbehavior = 0; stats.nodeStateStats.nSyncHeight = -1; stats.nodeStateStats.nCommonHeight = -1; stats.fNodeStateStatsAvailable = false; - pnode->copyStats(stats.nodeStats); + stats.nodeStats = nodestats; cachedNodeStats.append(stats); } } @@ -114,7 +113,7 @@ PeerTableModel::PeerTableModel(ClientModel *parent) : clientModel(parent), timer(0) { - columns << tr("Node/Service") << tr("User Agent") << tr("Ping Time"); + columns << tr("NodeId") << tr("Node/Service") << tr("User Agent") << tr("Ping Time"); priv = new PeerTablePriv(); // default to unsorted priv->sortColumn = -1; @@ -160,6 +159,8 @@ QVariant PeerTableModel::data(const QModelIndex &index, int role) const if (role == Qt::DisplayRole) { switch(index.column()) { + case NetNodeId: + return rec->nodeStats.nodeid; case Address: return QString::fromStdString(rec->nodeStats.addrName); case Subversion: diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index a2aaaa5d2..a4f7bbdb3 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -52,9 +52,10 @@ public: void stopAutoRefresh(); enum ColumnIndex { - Address = 0, - Subversion = 1, - Ping = 2 + NetNodeId = 0, + Address = 1, + Subversion = 2, + Ping = 3 }; /** @name Methods overridden from QAbstractTableModel diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 09d3e6387..4a0ce9fc7 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -918,34 +918,31 @@ void RPCConsole::showBanTableContextMenu(const QPoint& point) void RPCConsole::disconnectSelectedNode() { + if(!g_connman) + return; // Get currently selected peer address - QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address); + NodeId id = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::NetNodeId).toInt(); // Find the node, disconnect it and clear the selected node - if (CNode *bannedNode = FindNode(strNode.toStdString())) { - bannedNode->fDisconnect = true; + if(g_connman->DisconnectNode(id)) clearSelectedNode(); - } } void RPCConsole::banSelectedNode(int bantime) { - if (!clientModel) + if (!clientModel || !g_connman) return; // Get currently selected peer address - QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address); + QString strNode = GUIUtil::getEntryData(ui->peerWidget, 0, PeerTableModel::Address).toString(); // Find possible nodes, ban it and clear the selected node - if (FindNode(strNode.toStdString())) { - std::string nStr = strNode.toStdString(); - std::string addr; - int port = 0; - SplitHostPort(nStr, port, addr); + std::string nStr = strNode.toStdString(); + std::string addr; + int port = 0; + SplitHostPort(nStr, port, addr); - CNode::Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime); - - clearSelectedNode(); - clientModel->getBanTableModel()->refresh(); - } + g_connman->Ban(CNetAddr(addr), BanReasonManuallyAdded, bantime); + clearSelectedNode(); + clientModel->getBanTableModel()->refresh(); } void RPCConsole::unbanSelectedNode() @@ -954,12 +951,12 @@ void RPCConsole::unbanSelectedNode() return; // Get currently selected ban address - QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address); + QString strNode = GUIUtil::getEntryData(ui->banlistWidget, 0, BanTableModel::Address).toString(); CSubNet possibleSubnet(strNode.toStdString()); - if (possibleSubnet.IsValid()) + if (possibleSubnet.IsValid() && g_connman) { - CNode::Unban(possibleSubnet); + g_connman->Unban(possibleSubnet); clientModel->getBanTableModel()->refresh(); } } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 8261d685d..22149629d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -380,7 +380,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran CReserveKey *keyChange = transaction.getPossibleKeyChange(); - if(!wallet->CommitTransaction(*newTx, *keyChange, recipients[0].fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) + if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), recipients[0].fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) return TransactionCommitFailed; CTransaction* t = (CTransaction*)newTx; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 485d5333c..5a712a4e6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1007,7 +1007,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state, Params()); + ActivateBestChain(state, Params(), NULL, g_connman.get()); } if (!state.IsValid()) { @@ -1046,7 +1046,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state, Params()); + ActivateBestChain(state, Params(), NULL, g_connman.get()); } if (!state.IsValid()) { diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 669855062..0200b0816 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -168,7 +168,7 @@ UniValue gobject(const UniValue& params, bool fHelp) // -- make our change address CReserveKey reservekey(pwalletMain); // -- send the tx to the network - pwalletMain->CommitTransaction(wtx, reservekey, NetMsgType::TX); + pwalletMain->CommitTransaction(wtx, reservekey, g_connman.get(), NetMsgType::TX); DBG( cout << "gobject: prepare " << " strData = " << govobj.GetDataAsString() diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 8c1bd1349..88b4859da 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -44,7 +44,7 @@ UniValue privatesend(const UniValue& params, bool fHelp) return "Mixing is not supported from masternodes"; privateSendClient.fEnablePrivateSend = true; - bool result = privateSendClient.DoAutomaticDenominating(); + bool result = privateSendClient.DoAutomaticDenominating(*g_connman); return "Mixing " + (result ? "started successfully" : ("start failed: " + privateSendClient.GetStatus() + ", will retry")); } @@ -147,7 +147,8 @@ UniValue masternode(const UniValue& params, bool fHelp) CService addr = CService(strAddress); - CNode *pnode = ConnectNode(CAddress(addr, NODE_NETWORK), NULL); + // TODO: Pass CConnman instance somehow and don't use global variable. + CNode *pnode = g_connman->ConnectNode(CAddress(addr, NODE_NETWORK), NULL); if(!pnode) throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Couldn't connect to masternode %s", strAddress)); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index c53ee4b09..53661d0bb 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -178,7 +178,7 @@ UniValue generate(const UniValue& params, bool fHelp) ++pblock->nNonce; } CValidationState state; - if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL)) + if (!ProcessNewBlock(state, Params(), NULL, pblock, true, NULL, g_connman.get())) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); @@ -228,7 +228,7 @@ UniValue setgenerate(const UniValue& params, bool fHelp) mapArgs["-gen"] = (fGenerate ? "1" : "0"); mapArgs ["-genproclimit"] = itostr(nGenProcLimit); - GenerateBitcoins(fGenerate, nGenProcLimit, Params()); + GenerateBitcoins(fGenerate, nGenProcLimit, Params(), *g_connman); return NullUniValue; } @@ -490,7 +490,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (strMode != "template") throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); - if (vNodes.empty()) + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Dash Core is not connected!"); if (IsInitialBlockDownload()) @@ -794,7 +797,7 @@ UniValue submitblock(const UniValue& params, bool fHelp) CValidationState state; submitblock_StateCatcher sc(block.GetHash()); RegisterValidationInterface(&sc); - bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL); + bool fAccepted = ProcessNewBlock(state, Params(), NULL, &block, true, NULL, g_connman.get()); UnregisterValidationInterface(&sc); if (fBlockPresent) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index d41b6df37..47bc53458 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -97,7 +97,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) #endif obj.push_back(Pair("blocks", (int)chainActive.Height())); obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); + if(g_connman) + obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); obj.push_back(Pair("difficulty", (double)GetDifficulty())); obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); @@ -508,14 +509,16 @@ UniValue setmocktime(const UniValue& params, bool fHelp) // atomically with the time change to prevent peers from being // disconnected because we think we haven't communicated with them // in a long time. - LOCK2(cs_main, cs_vNodes); + LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); SetMockTime(params[0].get_int64()); uint64_t t = GetTime(); - BOOST_FOREACH(CNode* pnode, vNodes) { - pnode->nLastSend = pnode->nLastRecv = t; + if(g_connman) { + g_connman->ForEachNode([t](CNode* pnode) { + pnode->nLastSend = pnode->nLastRecv = t; + }); } return NullUniValue; diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 3d58b7882..9ae97872d 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -37,9 +37,10 @@ UniValue getconnectioncount(const UniValue& params, bool fHelp) + HelpExampleRpc("getconnectioncount", "") ); - LOCK2(cs_main, cs_vNodes); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - return (int)vNodes.size(); + return (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL); } UniValue ping(const UniValue& params, bool fHelp) @@ -55,29 +56,16 @@ UniValue ping(const UniValue& params, bool fHelp) + HelpExampleRpc("ping", "") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + // Request that each node send a ping during next message processing pass - LOCK2(cs_main, cs_vNodes); - - BOOST_FOREACH(CNode* pNode, vNodes) { - pNode->fPingQueued = true; - } - + g_connman->ForEachNode([](CNode* pnode) { + pnode->fPingQueued = true; + }); return NullUniValue; } -static void CopyNodeStats(std::vector& vstats) -{ - vstats.clear(); - - LOCK(cs_vNodes); - vstats.reserve(vNodes.size()); - BOOST_FOREACH(CNode* pnode, vNodes) { - CNodeStats stats; - pnode->copyStats(stats); - vstats.push_back(stats); - } -} - UniValue getpeerinfo(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) @@ -128,10 +116,11 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) + HelpExampleRpc("getpeerinfo", "") ); - LOCK(cs_main); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); vector vstats; - CopyNodeStats(vstats); + g_connman->GetNodeStats(vstats); UniValue ret(UniValue::VARR); @@ -213,32 +202,27 @@ UniValue addnode(const UniValue& params, bool fHelp) + HelpExampleRpc("addnode", "\"192.168.0.6:9999\", \"onetry\"") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + string strNode = params[0].get_str(); if (strCommand == "onetry") { CAddress addr; - OpenNetworkConnection(addr, NULL, strNode.c_str()); + g_connman->OpenNetworkConnection(addr, NULL, strNode.c_str()); return NullUniValue; } - LOCK(cs_vAddedNodes); - vector::iterator it = vAddedNodes.begin(); - for(; it != vAddedNodes.end(); it++) - if (strNode == *it) - break; - if (strCommand == "add") { - if (it != vAddedNodes.end()) + if(!g_connman->AddNode(strNode)) throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); - vAddedNodes.push_back(strNode); } else if(strCommand == "remove") { - if (it == vAddedNodes.end()) + if(!g_connman->RemoveAddedNode(strNode)) throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); - vAddedNodes.erase(it); } return NullUniValue; @@ -257,11 +241,12 @@ UniValue disconnectnode(const UniValue& params, bool fHelp) + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") ); - CNode* pNode = FindNode(params[0].get_str()); - if (pNode == NULL) - throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - pNode->fDisconnect = true; + bool ret = g_connman->DisconnectNode(params[0].get_str()); + if (!ret) + throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); return NullUniValue; } @@ -296,7 +281,10 @@ UniValue getaddednodeinfo(const UniValue& params, bool fHelp) + HelpExampleRpc("getaddednodeinfo", "true, \"192.168.0.201\"") ); - std::vector vInfo = GetAddedNodeInfo(); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + + std::vector vInfo = g_connman->GetAddedNodeInfo(); if (params.size() == 2) { bool found = false; @@ -358,19 +346,21 @@ UniValue getnettotals(const UniValue& params, bool fHelp) + HelpExampleCli("getnettotals", "") + HelpExampleRpc("getnettotals", "") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("totalbytesrecv", CNode::GetTotalBytesRecv())); - obj.push_back(Pair("totalbytessent", CNode::GetTotalBytesSent())); + obj.push_back(Pair("totalbytesrecv", g_connman->GetTotalBytesRecv())); + obj.push_back(Pair("totalbytessent", g_connman->GetTotalBytesSent())); obj.push_back(Pair("timemillis", GetTimeMillis())); UniValue outboundLimit(UniValue::VOBJ); - outboundLimit.push_back(Pair("timeframe", CNode::GetMaxOutboundTimeframe())); - outboundLimit.push_back(Pair("target", CNode::GetMaxOutboundTarget())); - outboundLimit.push_back(Pair("target_reached", CNode::OutboundTargetReached(false))); - outboundLimit.push_back(Pair("serve_historical_blocks", !CNode::OutboundTargetReached(true))); - outboundLimit.push_back(Pair("bytes_left_in_cycle", CNode::GetOutboundTargetBytesLeft())); - outboundLimit.push_back(Pair("time_left_in_cycle", CNode::GetMaxOutboundTimeLeftInCycle())); + outboundLimit.push_back(Pair("timeframe", g_connman->GetMaxOutboundTimeframe())); + outboundLimit.push_back(Pair("target", g_connman->GetMaxOutboundTarget())); + outboundLimit.push_back(Pair("target_reached", g_connman->OutboundTargetReached(false))); + outboundLimit.push_back(Pair("serve_historical_blocks", !g_connman->OutboundTargetReached(true))); + outboundLimit.push_back(Pair("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft())); + outboundLimit.push_back(Pair("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle())); obj.push_back(Pair("uploadtarget", outboundLimit)); return obj; } @@ -436,14 +426,15 @@ UniValue getnetworkinfo(const UniValue& params, bool fHelp) ); LOCK(cs_main); - UniValue obj(UniValue::VOBJ); obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("subversion", strSubVersion)); obj.push_back(Pair("protocolversion",PROTOCOL_VERSION)); - obj.push_back(Pair("localservices", strprintf("%016x", nLocalServices))); + if(g_connman) + obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices()))); obj.push_back(Pair("timeoffset", GetTimeOffset())); - obj.push_back(Pair("connections", (int)vNodes.size())); + if(g_connman) + obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); obj.push_back(Pair("networks", GetNetworksInfo())); obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); UniValue localAddresses(UniValue::VARR); @@ -483,6 +474,8 @@ UniValue setban(const UniValue& params, bool fHelp) + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\" 86400") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); CSubNet subNet; CNetAddr netAddr; @@ -501,7 +494,7 @@ UniValue setban(const UniValue& params, bool fHelp) if (strCommand == "add") { - if (isSubnet ? CNode::IsBanned(subNet) : CNode::IsBanned(netAddr)) + if (isSubnet ? g_connman->IsBanned(subNet) : g_connman->IsBanned(netAddr)) throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); int64_t banTime = 0; //use standard bantime if not specified @@ -512,11 +505,11 @@ UniValue setban(const UniValue& params, bool fHelp) if (params.size() == 4 && params[3].isTrue()) absolute = true; - isSubnet ? CNode::Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : CNode::Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); + isSubnet ? g_connman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute) : g_connman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); } else if(strCommand == "remove") { - if (!( isSubnet ? CNode::Unban(subNet) : CNode::Unban(netAddr) )) + if (!( isSubnet ? g_connman->Unban(subNet) : g_connman->Unban(netAddr) )) throw JSONRPCError(RPC_MISC_ERROR, "Error: Unban failed"); } return NullUniValue; @@ -533,8 +526,11 @@ UniValue listbanned(const UniValue& params, bool fHelp) + HelpExampleRpc("listbanned", "") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + banmap_t banMap; - CNode::GetBanned(banMap); + g_connman->GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); for (banmap_t::iterator it = banMap.begin(); it != banMap.end(); it++) @@ -562,8 +558,10 @@ UniValue clearbanned(const UniValue& params, bool fHelp) + HelpExampleCli("clearbanned", "") + HelpExampleRpc("clearbanned", "") ); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); - CNode::ClearBanned(); + g_connman->ClearBanned(); return NullUniValue; } diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 45ca79a56..90d635463 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -63,6 +63,7 @@ enum RPCErrorCode RPC_CLIENT_NODE_NOT_ADDED = -24, //! Node has not been added before RPC_CLIENT_NODE_NOT_CONNECTED = -29, //! Node to disconnect not found in connected nodes RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //! Invalid IP/Subnet + RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found //! Wallet errors RPC_WALLET_ERROR = -4, //! Unspecified problem with wallet (key not found etc.) diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index b354ffa6e..f630082fc 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -883,7 +883,13 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) if (fInstantSend && !instantsend.ProcessTxLockRequest(tx)) { throw JSONRPCError(RPC_TRANSACTION_ERROR, "Not a valid InstantSend transaction, see debug.log for more info"); } - RelayTransaction(tx); + if(!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + CInv inv(MSG_TX, hashTx); + g_connman->ForEachNode([&inv](CNode* pnode) + { + pnode->PushInventory(inv); + }); return hashTx.GetHex(); } diff --git a/src/sendalert.cpp b/src/sendalert.cpp index d3a57dcb5..53cd3fc84 100644 --- a/src/sendalert.cpp +++ b/src/sendalert.cpp @@ -23,7 +23,7 @@ If you screw up something, send another alert with nCancel set to cancel the bad alert. */ -void ThreadSendAlert() +void ThreadSendAlert(CConnman& connman) { if (!mapArgs.count("-sendalert") && !mapArgs.count("-printalert")) return; @@ -91,7 +91,7 @@ void ThreadSendAlert() // Confirm if (!mapArgs.count("-sendalert")) return; - while (vNodes.empty() && !ShutdownRequested()) + while (connman.GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && !ShutdownRequested()) MilliSleep(500); if (ShutdownRequested()) return; @@ -100,15 +100,13 @@ void ThreadSendAlert() printf("ThreadSendAlert() : Sending alert\n"); int nSent = 0; { - LOCK(cs_vNodes); - BOOST_FOREACH(CNode* pnode, vNodes) - { + g_connman->ForEachNode([&alert2, &nSent](CNode* pnode) { if (alert2.RelayTo(pnode)) { printf("ThreadSendAlert() : Sent alert to %s\n", pnode->addr.ToString().c_str()); nSent++; } - } + }); } printf("ThreadSendAlert() : Alert sent to %d nodes\n", nSent); } diff --git a/src/spork.cpp b/src/spork.cpp index 15d92d527..788f7ac08 100644 --- a/src/spork.cpp +++ b/src/spork.cpp @@ -259,5 +259,5 @@ bool CSporkMessage::CheckSignature() void CSporkMessage::Relay() { CInv inv(MSG_SPORK, GetHash()); - RelayInv(inv); + g_connman->RelayInv(inv); } diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index da26c3597..cc65784d2 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -40,69 +40,75 @@ CService ip(uint32_t i) return CService(CNetAddr(s), Params().GetDefaultPort()); } +static NodeId id = 0; + BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) BOOST_AUTO_TEST_CASE(DoS_banning) { - CNode::ClearBanned(); + connman->ClearBanned(); CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(INVALID_SOCKET, addr1, "", true); + CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, "", true); + GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - SendMessages(&dummyNode1); - BOOST_CHECK(CNode::IsBanned(addr1)); - BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned + SendMessages(&dummyNode1, *connman); + BOOST_CHECK(connman->IsBanned(addr1)); + BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002), NODE_NONE); - CNode dummyNode2(INVALID_SOCKET, addr2, "", true); + CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, "", true); + GetNodeSignals().InitializeNode(dummyNode2.GetId(), &dummyNode2); dummyNode2.nVersion = 1; Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2); - BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet... - BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be + SendMessages(&dummyNode2, *connman); + BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... + BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2); - BOOST_CHECK(CNode::IsBanned(addr2)); + SendMessages(&dummyNode2, *connman); + BOOST_CHECK(connman->IsBanned(addr2)); } BOOST_AUTO_TEST_CASE(DoS_banscore) { - CNode::ClearBanned(); + connman->ClearBanned(); mapArgs["-banscore"] = "111"; // because 11 is my favorite number CAddress addr1(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode1(INVALID_SOCKET, addr1, "", true); + CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, "", true); + GetNodeSignals().InitializeNode(dummyNode1.GetId(), &dummyNode1); dummyNode1.nVersion = 1; Misbehaving(dummyNode1.GetId(), 100); - SendMessages(&dummyNode1); - BOOST_CHECK(!CNode::IsBanned(addr1)); + SendMessages(&dummyNode1, *connman); + BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - SendMessages(&dummyNode1); - BOOST_CHECK(!CNode::IsBanned(addr1)); + SendMessages(&dummyNode1, *connman); + BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - SendMessages(&dummyNode1); - BOOST_CHECK(CNode::IsBanned(addr1)); + SendMessages(&dummyNode1, *connman); + BOOST_CHECK(connman->IsBanned(addr1)); mapArgs.erase("-banscore"); } BOOST_AUTO_TEST_CASE(DoS_bantime) { - CNode::ClearBanned(); + connman->ClearBanned(); int64_t nStartTime = GetTime(); SetMockTime(nStartTime); // Overrides future calls to GetTime() CAddress addr(ip(0xa0b0c001), NODE_NONE); - CNode dummyNode(INVALID_SOCKET, addr, "", true); + CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, "", true); + GetNodeSignals().InitializeNode(dummyNode.GetId(), &dummyNode); dummyNode.nVersion = 1; Misbehaving(dummyNode.GetId(), 100); - SendMessages(&dummyNode); - BOOST_CHECK(CNode::IsBanned(addr)); + SendMessages(&dummyNode, *connman); + BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); - BOOST_CHECK(CNode::IsBanned(addr)); + BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60*24+1); - BOOST_CHECK(!CNode::IsBanned(addr)); + BOOST_CHECK(!connman->IsBanned(addr)); } CTransaction RandomOrphan() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 4ccde505c..b583215a9 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); pblock->nNonce = blockinfo[i].nonce; CValidationState state; - BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL)); + BOOST_CHECK(ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL, connman)); BOOST_CHECK(state.IsValid()); pblock->hashPrevBlock = pblock->GetHash(); } diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index e32568dd8..60c58fd60 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -145,6 +145,8 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) BOOST_AUTO_TEST_CASE(cnode_simple_test) { SOCKET hSocket = INVALID_SOCKET; + NodeId id = 0; + int height = 0; in_addr ipv4Addr; ipv4Addr.s_addr = 0xa0b0c001; @@ -154,12 +156,12 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) bool fInboundIn = false; // Test that fFeeler is false by default. - CNode* pnode1 = new CNode(hSocket, addr, pszDest, fInboundIn); + CNode* pnode1 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, pszDest, fInboundIn); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; - CNode* pnode2 = new CNode(hSocket, addr, pszDest, fInboundIn); + CNode* pnode2 = new CNode(id++, NODE_NETWORK, height, hSocket, addr, pszDest, fInboundIn); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } diff --git a/src/test/test_dash.cpp b/src/test/test_dash.cpp index 003ae33fc..e52257934 100644 --- a/src/test/test_dash.cpp +++ b/src/test/test_dash.cpp @@ -30,6 +30,8 @@ CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h CWallet* pwalletMain; +std::unique_ptr g_connman; + extern bool fPrintToConsole; extern void noui_connect(); @@ -47,6 +49,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); + g_connman.reset(); } TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) @@ -72,6 +75,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); + g_connman = std::unique_ptr(new CConnman()); + connman = g_connman.get(); RegisterNodeSignals(GetNodeSignals()); } @@ -131,7 +136,7 @@ TestChain100Setup::CreateAndProcessBlock(const std::vector& while (!CheckProofOfWork(block.GetHash(), block.nBits, chainparams.GetConsensus())) ++block.nNonce; CValidationState state; - ProcessNewBlock(state, chainparams, NULL, &block, true, NULL); + ProcessNewBlock(state, chainparams, NULL, &block, true, NULL, connman); CBlock result = block; delete pblocktemplate; diff --git a/src/test/test_dash.h b/src/test/test_dash.h index 8652c9aa3..36d2601b3 100644 --- a/src/test/test_dash.h +++ b/src/test/test_dash.h @@ -24,10 +24,12 @@ struct BasicTestingSetup { * Included are data directory, coins database, script check threads * and wallet (if enabled) setup. */ +class CConnman; struct TestingSetup: public BasicTestingSetup { CCoinsViewDB *pcoinsdbview; boost::filesystem::path pathTemp; boost::thread_group threadGroup; + CConnman* connman; TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index b6c5752ce..1ae2705ca 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -19,7 +19,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); - g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); + g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.ScriptForMining.connect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockFound.connect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); @@ -29,7 +29,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.BlockFound.disconnect(boost::bind(&CValidationInterface::ResetRequestCount, pwalletIn, _1)); g_signals.ScriptForMining.disconnect(boost::bind(&CValidationInterface::GetScriptForMining, pwalletIn, _1)); g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); - g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); + g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2)); g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); diff --git a/src/validationinterface.h b/src/validationinterface.h index d621fd709..e20c3d614 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -12,6 +12,7 @@ class CBlock; struct CBlockLocator; class CBlockIndex; +class CConnman; class CReserveScript; class CTransaction; class CValidationInterface; @@ -37,7 +38,7 @@ protected: virtual void SetBestChain(const CBlockLocator &locator) {} virtual bool UpdatedTransaction(const uint256 &hash) { return false;} virtual void Inventory(const uint256 &hash) {} - virtual void ResendWalletTransactions(int64_t nBestBlockTime) {} + virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {} virtual void BlockChecked(const CBlock&, const CValidationState&) {} virtual void GetScriptForMining(boost::shared_ptr&) {}; virtual void ResetRequestCount(const uint256 &hash) {}; @@ -60,7 +61,7 @@ struct CMainSignals { /** Notifies listeners about an inventory item being seen on the network. */ boost::signals2::signal Inventory; /** Tells listeners to broadcast their data. */ - boost::signals2::signal Broadcast; + boost::signals2::signal Broadcast; /** Notifies listeners of a block validation result */ boost::signals2::signal BlockChecked; /** Notifies listeners that a key for mining is required (coinbase) */ diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e368c4407..6c1062cd4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -382,6 +382,9 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr if (nValue > curBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + // Parse Dash address CScript scriptPubKey = GetScriptForDestination(address); @@ -399,7 +402,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } - if (!pwalletMain->CommitTransaction(wtxNew, reservekey, fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) + if (!pwalletMain->CommitTransaction(wtxNew, reservekey, g_connman.get(), fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } @@ -1053,6 +1056,9 @@ UniValue sendmany(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + if (pwalletMain->GetBroadcastTransactions() && !g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + string strAccount = AccountFromValue(params[0]); UniValue sendTo = params[1].get_obj(); int nMinDepth = 1; @@ -1123,7 +1129,7 @@ UniValue sendmany(const UniValue& params, bool fHelp) NULL, true, fUsePrivateSend ? ONLY_DENOMINATED : ALL_COINS, fUseInstantSend); if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); - if (!pwalletMain->CommitTransaction(wtx, keyChange, fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) + if (!pwalletMain->CommitTransaction(wtx, keyChange, g_connman.get(), fUseInstantSend ? NetMsgType::TXLOCKREQUEST : NetMsgType::TX)) throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); return wtx.GetHash().GetHex(); @@ -2503,9 +2509,12 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp) "Returns array of transaction ids that were re-broadcast.\n" ); + if (!g_connman) + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + LOCK2(cs_main, pwalletMain->cs_wallet); - std::vector txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); + std::vector txids = pwalletMain->ResendWalletTransactionsBefore(GetTime(), g_connman.get()); UniValue result(UniValue::VARR); BOOST_FOREACH(const uint256& txid, txids) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 61f3b3032..d553f0175 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1751,7 +1751,7 @@ void CWallet::ReacceptWalletTransactions() } } -bool CWalletTx::RelayWalletTransaction(std::string strCommand) +bool CWalletTx::RelayWalletTransaction(CConnman* connman, std::string strCommand) { assert(pwallet->GetBroadcastTransactions()); if (!IsCoinBase()) @@ -1763,8 +1763,10 @@ bool CWalletTx::RelayWalletTransaction(std::string strCommand) if(strCommand == NetMsgType::TXLOCKREQUEST) { instantsend.ProcessTxLockRequest(((CTxLockRequest)*this)); } - RelayTransaction((CTransaction)*this); - return true; + if (connman) { + connman->RelayTransaction((CTransaction)*this); + return true; + } } } return false; @@ -2070,7 +2072,7 @@ bool CWalletTx::IsEquivalentTo(const CWalletTx& tx) const return CTransaction(tx1) == CTransaction(tx2); } -std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) +std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) { std::vector result; @@ -2088,13 +2090,13 @@ std::vector CWallet::ResendWalletTransactionsBefore(int64_t nTime) BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; - if (wtx.RelayWalletTransaction()) + if (wtx.RelayWalletTransaction(connman)) result.push_back(wtx.GetHash()); } return result; } -void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) +void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) { // Do this infrequently and randomly to avoid giving away // that these are our transactions. @@ -2112,7 +2114,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime) // Rebroadcast unconfirmed txes older than 5 minutes before the last // block was found: - std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60); + std::vector relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman); if (!relayed.empty()) LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); } @@ -3528,7 +3530,7 @@ bool CWallet::CreateTransaction(const vector& vecSend, CWalletTx& wt /** * Call after CreateTransaction unless you want to abort */ -bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand) +bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, std::string strCommand) { { LOCK2(cs_main, cs_wallet); @@ -3574,7 +3576,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std: LogPrintf("CommitTransaction(): Error: Transaction not valid\n"); return false; } - wtxNew.RelayWalletTransaction(strCommand); + wtxNew.RelayWalletTransaction(connman, strCommand); } } return true; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 8cdfb44ca..36252f109 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -468,7 +468,7 @@ public: int64_t GetTxTime() const; int GetRequestCount() const; - bool RelayWalletTransaction(std::string strCommand="tx"); + bool RelayWalletTransaction(CConnman* connman, std::string strCommand="tx"); std::set GetConflicts() const; }; @@ -862,8 +862,8 @@ public: bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); - void ResendWalletTransactions(int64_t nBestBlockTime); - std::vector ResendWalletTransactionsBefore(int64_t nTime); + void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman); + std::vector ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); CAmount GetBalance() const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; @@ -893,7 +893,7 @@ public: */ bool CreateTransaction(const std::vector& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true, AvailableCoinsType nCoinType=ALL_COINS, bool fUseInstantSend=false); - bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, std::string strCommand="tx"); + bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, std::string strCommand="tx"); bool CreateCollateralTransaction(CMutableTransaction& txCollateral, std::string& strReason); bool ConvertList(std::vector vecTxIn, std::vector& vecAmounts);