aboutsummaryrefslogtreecommitdiff
path: root/src/net.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/net.h')
-rw-r--r--src/net.h1138
1 files changed, 636 insertions, 502 deletions
diff --git a/src/net.h b/src/net.h
index 729b1bcd57..8378a303b8 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1,76 +1,475 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin developers
-// Distributed under the MIT/X11 software license, see the accompanying
+// Copyright (c) 2009-2017 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_NET_H
#define BITCOIN_NET_H
-#include "bloom.h"
-#include "compat.h"
-#include "hash.h"
-#include "limitedmap.h"
-#include "mruset.h"
-#include "netbase.h"
-#include "protocol.h"
-#include "sync.h"
-#include "uint256.h"
-#include "util.h"
-
+#include <addrdb.h>
+#include <addrman.h>
+#include <amount.h>
+#include <bloom.h>
+#include <compat.h>
+#include <hash.h>
+#include <limitedmap.h>
+#include <netaddress.h>
+#include <policy/feerate.h>
+#include <protocol.h>
+#include <random.h>
+#include <streams.h>
+#include <sync.h>
+#include <uint256.h>
+#include <threadinterrupt.h>
+
+#include <atomic>
#include <deque>
#include <stdint.h>
+#include <thread>
+#include <memory>
+#include <condition_variable>
#ifndef WIN32
#include <arpa/inet.h>
#endif
-#include <boost/foreach.hpp>
-#include <boost/signals2/signal.hpp>
-#include <openssl/rand.h>
-class CAddrMan;
-class CBlockIndex;
+class CScheduler;
class CNode;
-namespace boost {
- class thread_group;
-}
-
+/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
+static const int PING_INTERVAL = 2 * 60;
+/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
+static const int TIMEOUT_INTERVAL = 20 * 60;
+/** Run the feeler connection loop once every 2 minutes or 120 seconds. **/
+static const int FEELER_INTERVAL = 120;
/** The maximum number of entries in an 'inv' protocol message */
static const unsigned int MAX_INV_SZ = 50000;
+/** The maximum number of new addresses to accumulate before announcing. */
+static const unsigned int MAX_ADDR_TO_SEND = 1000;
+/** Maximum length of incoming protocol messages (no message over 4 MB is currently acceptable). */
+static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 4 * 1000 * 1000;
+/** Maximum length of strSubVer in `version` message */
+static const unsigned int MAX_SUBVERSION_LENGTH = 256;
+/** Maximum number of automatic outgoing nodes */
+static const int MAX_OUTBOUND_CONNECTIONS = 8;
+/** Maximum number of addnode outgoing nodes */
+static const int MAX_ADDNODE_CONNECTIONS = 8;
+/** -listen default */
+static const bool DEFAULT_LISTEN = true;
+/** -upnp default */
+#ifdef USE_UPNP
+static const bool DEFAULT_UPNP = USE_UPNP;
+#else
+static const bool DEFAULT_UPNP = false;
+#endif
+/** The maximum number of entries in mapAskFor */
+static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
+/** The maximum number of entries in setAskFor (larger due to getdata latency)*/
+static const size_t SETASKFOR_MAX_SZ = 2 * MAX_INV_SZ;
+/** The maximum number of peer connections to maintain. */
+static const unsigned int DEFAULT_MAX_PEER_CONNECTIONS = 125;
+/** The default for -maxuploadtarget. 0 = Unlimited */
+static const uint64_t DEFAULT_MAX_UPLOAD_TARGET = 0;
+/** The default timeframe for -maxuploadtarget. 1 day. */
+static const uint64_t MAX_UPLOAD_TIMEFRAME = 60 * 60 * 24;
+/** Default for blocks only*/
+static const bool DEFAULT_BLOCKSONLY = false;
+
+static const bool DEFAULT_FORCEDNSSEED = false;
+static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
+static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
+
+// NOTE: When adjusting this, update rpcnet:setban's help ("24h")
+static const unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
+
+typedef int64_t NodeId;
+
+struct AddedNodeInfo
+{
+ std::string strAddedNode;
+ CService resolvedAddress;
+ bool fConnected;
+ bool fInbound;
+};
-inline unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
-inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
-
-void AddOneShot(std::string strDest);
-bool RecvLine(SOCKET hSocket, std::string& strLine);
-bool GetMyExternalIP(CNetAddr& ipRet);
-void AddressCurrentlyConnected(const CService& addr);
-CNode* FindNode(const CNetAddr& ip);
-CNode* FindNode(const CService& ip);
-CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL);
-void MapPort(bool fUseUPnP);
-unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
-void StartNode(boost::thread_group& threadGroup);
-bool StopNode();
-void SocketSendData(CNode *pnode);
+class CNodeStats;
+class CClientUIInterface;
-typedef int NodeId;
+struct CSerializedNetMsg
+{
+ CSerializedNetMsg() = default;
+ CSerializedNetMsg(CSerializedNetMsg&&) = default;
+ CSerializedNetMsg& operator=(CSerializedNetMsg&&) = default;
+ // No copying, only moves.
+ CSerializedNetMsg(const CSerializedNetMsg& msg) = delete;
+ CSerializedNetMsg& operator=(const CSerializedNetMsg&) = delete;
+
+ std::vector<unsigned char> data;
+ std::string command;
+};
-// Signals for message handling
-struct CNodeSignals
+class NetEventsInterface;
+class CConnman
{
- boost::signals2::signal<int ()> GetHeight;
- boost::signals2::signal<bool (CNode*)> ProcessMessages;
- boost::signals2::signal<bool (CNode*, bool)> SendMessages;
- boost::signals2::signal<void (NodeId, const CNode*)> InitializeNode;
- boost::signals2::signal<void (NodeId)> FinalizeNode;
+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;
+ int nMaxConnections = 0;
+ int nMaxOutbound = 0;
+ int nMaxAddnode = 0;
+ int nMaxFeeler = 0;
+ int nBestHeight = 0;
+ CClientUIInterface* uiInterface = nullptr;
+ NetEventsInterface* m_msgproc = nullptr;
+ unsigned int nSendBufferMaxSize = 0;
+ unsigned int nReceiveFloodSize = 0;
+ uint64_t nMaxOutboundTimeframe = 0;
+ uint64_t nMaxOutboundLimit = 0;
+ std::vector<std::string> vSeedNodes;
+ std::vector<CSubNet> vWhitelistedRange;
+ std::vector<CService> vBinds, vWhiteBinds;
+ bool m_use_addrman_outgoing = true;
+ std::vector<std::string> m_specified_outgoing;
+ std::vector<std::string> m_added_nodes;
+ };
+
+ void Init(const Options& connOptions) {
+ nLocalServices = connOptions.nLocalServices;
+ nMaxConnections = connOptions.nMaxConnections;
+ nMaxOutbound = std::min(connOptions.nMaxOutbound, connOptions.nMaxConnections);
+ nMaxAddnode = connOptions.nMaxAddnode;
+ nMaxFeeler = connOptions.nMaxFeeler;
+ nBestHeight = connOptions.nBestHeight;
+ clientInterface = connOptions.uiInterface;
+ m_msgproc = connOptions.m_msgproc;
+ nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
+ nReceiveFloodSize = connOptions.nReceiveFloodSize;
+ {
+ LOCK(cs_totalBytesSent);
+ nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
+ nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
+ }
+ vWhitelistedRange = connOptions.vWhitelistedRange;
+ {
+ LOCK(cs_vAddedNodes);
+ vAddedNodes = connOptions.m_added_nodes;
+ }
+ }
+
+ CConnman(uint64_t seed0, uint64_t seed1);
+ ~CConnman();
+ bool Start(CScheduler& scheduler, const Options& options);
+ void Stop();
+ void Interrupt();
+ bool GetNetworkActive() const { return fNetworkActive; };
+ void SetNetworkActive(bool active);
+ void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CSemaphoreGrant *grantOutbound = nullptr, const char *strDest = nullptr, bool fOneShot = false, bool fFeeler = false, bool manual_connection = false);
+ bool CheckIncomingNonce(uint64_t nonce);
+
+ bool ForNode(NodeId id, std::function<bool(CNode* pnode)> func);
+
+ void PushMessage(CNode* pnode, CSerializedNetMsg&& msg);
+
+ template<typename Callable>
+ void ForEachNode(Callable&& func)
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes) {
+ if (NodeFullyConnected(node))
+ func(node);
+ }
+ };
+
+ template<typename Callable>
+ void ForEachNode(Callable&& func) const
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes) {
+ if (NodeFullyConnected(node))
+ func(node);
+ }
+ };
+
+ template<typename Callable, typename CallableAfter>
+ void ForEachNodeThen(Callable&& pre, CallableAfter&& post)
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes) {
+ if (NodeFullyConnected(node))
+ pre(node);
+ }
+ post();
+ };
+
+ template<typename Callable, typename CallableAfter>
+ void ForEachNodeThen(Callable&& pre, CallableAfter&& post) const
+ {
+ LOCK(cs_vNodes);
+ for (auto&& node : vNodes) {
+ if (NodeFullyConnected(node))
+ pre(node);
+ }
+ post();
+ };
+
+ // Addrman functions
+ size_t GetAddressCount() const;
+ void SetServices(const CService &addr, ServiceFlags nServices);
+ void MarkAddressGood(const CAddress& addr);
+ void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
+ std::vector<CAddress> GetAddresses();
+
+ // 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);
+
+ // This allows temporarily exceeding nMaxOutbound, with the goal of finding
+ // a peer that is better than all our current peers.
+ void SetTryNewOutboundPeer(bool flag);
+ bool GetTryNewOutboundPeer();
+
+ // Return the number of outbound peers we have in excess of our target (eg,
+ // if we previously called SetTryNewOutboundPeer(true), and have since set
+ // to false, we may have extra peers that we wish to disconnect). This may
+ // return a value less than (num_outbound_connections - num_outbound_slots)
+ // in cases where some outbound connections are not yet fully connected, or
+ // not yet fully disconnected.
+ int GetExtraOutboundCount();
+
+ bool AddNode(const std::string& node);
+ bool RemoveAddedNode(const std::string& node);
+ std::vector<AddedNodeInfo> GetAddedNodeInfo();
+
+ size_t GetNodeCount(NumConnections num);
+ void GetNodeStats(std::vector<CNodeStats>& vstats);
+ bool DisconnectNode(const std::string& node);
+ bool DisconnectNode(NodeId id);
+
+ 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;
+
+ /** Get a unique deterministic randomizer. */
+ CSipHasher GetDeterministicRandomizer(uint64_t id) const;
+
+ unsigned int GetReceiveFloodSize() const;
+
+ void WakeMessageHandler();
+private:
+ struct ListenSocket {
+ SOCKET socket;
+ bool whitelisted;
+
+ ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
+ };
+
+ bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
+ bool Bind(const CService &addr, unsigned int flags);
+ bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
+ void ThreadOpenAddedConnections();
+ void AddOneShot(const std::string& strDest);
+ void ProcessOneShot();
+ void ThreadOpenConnections(std::vector<std::string> connect);
+ void ThreadMessageHandler();
+ void AcceptConnection(const ListenSocket& hListenSocket);
+ void ThreadSocketHandler();
+ void ThreadDNSAddressSeed();
+
+ uint64_t CalculateKeyedNetGroup(const CAddress& ad) const;
+
+ CNode* FindNode(const CNetAddr& ip);
+ CNode* FindNode(const CSubNet& subNet);
+ CNode* FindNode(const std::string& addrName);
+ CNode* FindNode(const CService& addr);
+
+ bool AttemptToEvictConnection();
+ CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure);
+ bool IsWhitelistedRange(const CNetAddr &addr);
+
+ void DeleteNode(CNode* pnode);
+
+ NodeId GetNewNodeId();
+
+ size_t SocketSendData(CNode *pnode) const;
+ //!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();
+
+ // Network stats
+ void RecordBytesRecv(uint64_t bytes);
+ void RecordBytesSent(uint64_t bytes);
+
+ // Whether the node should be passed out in ForEach* callbacks
+ static bool NodeFullyConnected(const CNode* pnode);
+
+ // Network usage totals
+ CCriticalSection cs_totalBytesRecv;
+ CCriticalSection cs_totalBytesSent;
+ uint64_t nTotalBytesRecv GUARDED_BY(cs_totalBytesRecv);
+ uint64_t nTotalBytesSent GUARDED_BY(cs_totalBytesSent);
+
+ // outbound limit & stats
+ uint64_t nMaxOutboundTotalBytesSentInCycle GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundCycleStartTime GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundLimit GUARDED_BY(cs_totalBytesSent);
+ uint64_t nMaxOutboundTimeframe GUARDED_BY(cs_totalBytesSent);
+
+ // Whitelisted ranges. Any node connecting from these is automatically
+ // whitelisted (as well as those connecting to whitelisted binds).
+ std::vector<CSubNet> vWhitelistedRange;
+
+ unsigned int nSendBufferMaxSize;
+ unsigned int nReceiveFloodSize;
+
+ std::vector<ListenSocket> vhListenSocket;
+ std::atomic<bool> fNetworkActive;
+ banmap_t setBanned;
+ CCriticalSection cs_setBanned;
+ bool setBannedIsDirty;
+ bool fAddressesInitialized;
+ CAddrMan addrman;
+ std::deque<std::string> vOneShots;
+ CCriticalSection cs_vOneShots;
+ std::vector<std::string> vAddedNodes GUARDED_BY(cs_vAddedNodes);
+ CCriticalSection cs_vAddedNodes;
+ std::vector<CNode*> vNodes;
+ std::list<CNode*> vNodesDisconnected;
+ mutable CCriticalSection cs_vNodes;
+ std::atomic<NodeId> nLastNodeId;
+
+ /** Services this instance offers */
+ ServiceFlags nLocalServices;
+
+ std::unique_ptr<CSemaphore> semOutbound;
+ std::unique_ptr<CSemaphore> semAddnode;
+ int nMaxConnections;
+ int nMaxOutbound;
+ int nMaxAddnode;
+ int nMaxFeeler;
+ std::atomic<int> nBestHeight;
+ CClientUIInterface* clientInterface;
+ NetEventsInterface* m_msgproc;
+
+ /** SipHasher seeds for deterministic randomness */
+ const uint64_t nSeed0, nSeed1;
+
+ /** flag for waking the message processor. */
+ bool fMsgProcWake;
+
+ std::condition_variable condMsgProc;
+ std::mutex mutexMsgProc;
+ std::atomic<bool> flagInterruptMsgProc;
+
+ CThreadInterrupt interruptNet;
+
+ std::thread threadDNSAddressSeed;
+ std::thread threadSocketHandler;
+ std::thread threadOpenAddedConnections;
+ std::thread threadOpenConnections;
+ std::thread threadMessageHandler;
+
+ /** flag for deciding to connect to an extra outbound peer,
+ * in excess of nMaxOutbound
+ * This takes the place of a feeler connection */
+ std::atomic_bool m_try_another_outbound_peer;
+
+ friend struct CConnmanTest;
};
+extern std::unique_ptr<CConnman> g_connman;
+void Discover();
+void StartMapPort();
+void InterruptMapPort();
+void StopMapPort();
+unsigned short GetListenPort();
+bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
+struct CombinerAll
+{
+ typedef bool result_type;
-CNodeSignals& GetNodeSignals();
+ template<typename I>
+ bool operator()(I first, I last) const
+ {
+ while (first != last) {
+ if (!(*first)) return false;
+ ++first;
+ }
+ return true;
+ }
+};
+/**
+ * Interface for message handling
+ */
+class NetEventsInterface
+{
+public:
+ virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0;
+ virtual void InitializeNode(CNode* pnode) = 0;
+ virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0;
+};
enum
{
@@ -78,43 +477,35 @@ enum
LOCAL_IF, // address a local interface listens on
LOCAL_BIND, // address explicit bound to
LOCAL_UPNP, // address reported by UPnP
- LOCAL_HTTP, // address reported by whatismyip.com and similar
LOCAL_MANUAL, // address explicitly specified (-externalip=)
LOCAL_MAX
};
+bool IsPeerAddrLocalGood(CNode *pnode);
+void AdvertiseLocal(CNode *pnode);
void SetLimited(enum Network net, bool fLimited = true);
bool IsLimited(enum Network net);
bool IsLimited(const CNetAddr& addr);
bool AddLocal(const CService& addr, int nScore = LOCAL_NONE);
bool AddLocal(const CNetAddr& addr, int nScore = LOCAL_NONE);
+bool RemoveLocal(const CService& addr);
bool SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
-bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
+bool GetLocal(CService &addr, const CNetAddr *paddrPeer = nullptr);
+bool IsReachable(enum Network net);
bool IsReachable(const CNetAddr &addr);
-void SetReachable(enum Network net, bool fFlag = true);
-CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
+CAddress GetLocalAddress(const CNetAddr *paddrPeer, ServiceFlags nLocalServices);
extern bool fDiscover;
-extern uint64_t nLocalServices;
-extern uint64_t nLocalHostNonce;
-extern CAddrMan addrman;
-extern int nMaxConnections;
-
-extern std::vector<CNode*> vNodes;
-extern CCriticalSection cs_vNodes;
-extern std::map<CInv, CDataStream> mapRelay;
-extern std::deque<std::pair<int64_t, CInv> > vRelayExpiration;
-extern CCriticalSection cs_mapRelay;
-extern limitedmap<CInv, int64_t> mapAlreadyAskedFor;
+extern bool fListen;
+extern bool fRelayTxes;
-extern std::vector<std::string> vAddedNodes;
-extern CCriticalSection cs_vAddedNodes;
+extern limitedmap<uint256, int64_t> mapAlreadyAskedFor;
-extern NodeId nLastNodeId;
-extern CCriticalSection cs_nLastNodeId;
+/** Subversion as sent to the P2P network in `version` messages */
+extern std::string strSubVersion;
struct LocalServiceInfo {
int nScore;
@@ -122,33 +513,48 @@ struct LocalServiceInfo {
};
extern CCriticalSection cs_mapLocalHost;
-extern map<CNetAddr, LocalServiceInfo> mapLocalHost;
+extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost;
+typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
class CNodeStats
{
public:
NodeId nodeid;
- uint64_t nServices;
+ ServiceFlags nServices;
+ bool fRelayTxes;
int64_t nLastSend;
int64_t nLastRecv;
int64_t nTimeConnected;
+ int64_t nTimeOffset;
std::string addrName;
int nVersion;
std::string cleanSubVer;
bool fInbound;
+ bool m_manual_connection;
int nStartingHeight;
uint64_t nSendBytes;
+ mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
- bool fSyncNode;
+ mapMsgCmdSize mapRecvBytesPerMsgCmd;
+ bool fWhitelisted;
double dPingTime;
double dPingWait;
+ double dMinPing;
+ // Our address, as reported by the peer
std::string addrLocal;
+ // Address of this peer
+ CAddress addr;
+ // Bind address of our side of the connection
+ CAddress addrBind;
};
class CNetMessage {
+private:
+ mutable CHash256 hasher;
+ mutable uint256 data_hash;
public:
bool in_data; // parsing header (false) or data (true)
@@ -159,11 +565,14 @@ public:
CDataStream vRecv; // received message data
unsigned int nDataPos;
- CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) {
+ int64_t nTime; // time (in microseconds) of message receipt.
+
+ CNetMessage(const CMessageHeader::MessageStartChars& pchMessageStartIn, int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), hdr(pchMessageStartIn), vRecv(nTypeIn, nVersionIn) {
hdrbuf.resize(24);
in_data = false;
nHdrPos = 0;
nDataPos = 0;
+ nTime = 0;
}
bool complete() const
@@ -173,6 +582,8 @@ public:
return (hdr.nMessageSize == nDataPos);
}
+ const uint256& GetMessageHash() const;
+
void SetVersion(int nVersionIn)
{
hdrbuf.SetVersion(nVersionIn);
@@ -184,196 +595,185 @@ public:
};
-
-
-
/** Information about a peer */
class CNode
{
+ friend class CConnman;
public:
// socket
- uint64_t nServices;
+ std::atomic<ServiceFlags> nServices;
SOCKET hSocket;
- CDataStream ssSend;
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
uint64_t nSendBytes;
- std::deque<CSerializeData> vSendMsg;
+ std::deque<std::vector<unsigned char>> vSendMsg;
CCriticalSection cs_vSend;
+ CCriticalSection cs_hSocket;
+ CCriticalSection cs_vRecv;
+
+ CCriticalSection cs_vProcessMsg;
+ std::list<CNetMessage> vProcessMsg;
+ size_t nProcessQueueSize;
+
+ CCriticalSection cs_sendProcessing;
std::deque<CInv> vRecvGetData;
- std::deque<CNetMessage> vRecvMsg;
- CCriticalSection cs_vRecvMsg;
uint64_t nRecvBytes;
- int nRecvVersion;
-
- int64_t nLastSend;
- int64_t nLastRecv;
- int64_t nLastSendEmpty;
- int64_t nTimeConnected;
- CAddress addr;
- std::string addrName;
- CService addrLocal;
- int nVersion;
+ std::atomic<int> nRecvVersion;
+
+ std::atomic<int64_t> nLastSend;
+ std::atomic<int64_t> nLastRecv;
+ const int64_t nTimeConnected;
+ std::atomic<int64_t> nTimeOffset;
+ // Address of this peer
+ const CAddress addr;
+ // Bind address of our side of the connection
+ const CAddress addrBind;
+ std::atomic<int> nVersion;
// strSubVer is whatever byte array we read from the wire. However, this field is intended
// to be printed out, displayed to humans in various forms and so on. So we sanitize it and
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
+ CCriticalSection cs_SubVer; // used for both cleanSubVer and strSubVer
+ bool fWhitelisted; // This peer can bypass DoS banning.
+ bool fFeeler; // If true this node is being used as a short lived feeler.
bool fOneShot;
+ bool m_manual_connection;
bool fClient;
- bool fInbound;
- bool fNetworkNode;
- bool fSuccessfullyConnected;
- bool fDisconnect;
+ const bool fInbound;
+ std::atomic_bool fSuccessfullyConnected;
+ std::atomic_bool fDisconnect;
// We use fRelayTxes for two purposes -
// a) it allows us to not relay tx invs before receiving the peer's version message
- // b) the peer may tell us in their version message that we should not relay tx invs
- // until they have initialized their bloom filter.
- bool fRelayTxes;
+ // b) the peer may tell us in its version message that we should not relay tx invs
+ // unless it loads a bloom filter.
+ bool fRelayTxes; //protected by cs_filter
+ bool fSentAddr;
CSemaphoreGrant grantOutbound;
CCriticalSection cs_filter;
- CBloomFilter* pfilter;
- int nRefCount;
- NodeId id;
-protected:
+ std::unique_ptr<CBloomFilter> pfilter;
+ std::atomic<int> nRefCount;
- // Denial-of-service detection/prevention
- // Key is IP address, value is banned-until-time
- static std::map<CNetAddr, int64_t> setBanned;
- static CCriticalSection cs_setBanned;
+ const uint64_t nKeyedNetGroup;
+ std::atomic_bool fPauseRecv;
+ std::atomic_bool fPauseSend;
+protected:
- // Basic fuzz-testing
- void Fuzz(int nChance); // modifies ssSend
+ mapMsgCmdSize mapSendBytesPerMsgCmd;
+ mapMsgCmdSize mapRecvBytesPerMsgCmd;
public:
uint256 hashContinue;
- CBlockIndex* pindexLastGetBlocksBegin;
- uint256 hashLastGetBlocksEnd;
- int nStartingHeight;
- bool fStartSync;
+ std::atomic<int> nStartingHeight;
// flood relay
std::vector<CAddress> vAddrToSend;
- std::set<CAddress> setAddrKnown;
+ CRollingBloomFilter addrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
+ int64_t nNextAddrSend;
+ int64_t nNextLocalAddrSend;
// inventory based relay
- mruset<CInv> setInventoryKnown;
- std::vector<CInv> vInventoryToSend;
+ CRollingBloomFilter filterInventoryKnown;
+ // Set of transaction ids we still have to announce.
+ // They are sorted by the mempool before relay, so the order is not important.
+ std::set<uint256> setInventoryTxToSend;
+ // List of block ids we still have announce.
+ // There is no final sorting before sending, as they are always sent immediately
+ // and in the order requested.
+ std::vector<uint256> vInventoryBlockToSend;
CCriticalSection cs_inventory;
+ std::set<uint256> setAskFor;
std::multimap<int64_t, CInv> mapAskFor;
+ int64_t nNextInvSend;
+ // Used for headers announcements - unfiltered blocks to relay
+ // Also protected by cs_inventory
+ std::vector<uint256> vBlockHashesToAnnounce;
+ // Used for BIP35 mempool sending, also protected by cs_inventory
+ bool fSendMempool;
+
+ // Last time a "MEMPOOL" request was serviced.
+ std::atomic<int64_t> timeLastMempoolReq;
+
+ // Block and TXN accept times
+ std::atomic<int64_t> nLastBlockTime;
+ std::atomic<int64_t> nLastTXTime;
+
+ // Ping time measurement:
+ // The pong reply we're expecting, or 0 if no pong expected.
+ std::atomic<uint64_t> nPingNonceSent;
+ // Time (in usec) the last ping was sent, or 0 if no ping was ever sent.
+ std::atomic<int64_t> nPingUsecStart;
+ // Last measured round-trip time.
+ std::atomic<int64_t> nPingUsecTime;
+ // Best measured round-trip time.
+ std::atomic<int64_t> nMinPingUsecTime;
+ // Whether a ping is requested.
+ std::atomic<bool> fPingQueued;
+ // Minimum fee rate with which to filter inv's to this node
+ CAmount minFeeFilter;
+ CCriticalSection cs_feeFilter;
+ CAmount lastSentFeeFilter;
+ int64_t nextSendTimeFeeFilter;
+
+ CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false);
+ ~CNode();
+ CNode(const CNode&) = delete;
+ CNode& operator=(const CNode&) = delete;
- // Ping time measurement
- uint64_t nPingNonceSent;
- int64_t nPingUsecStart;
- int64_t nPingUsecTime;
- bool fPingQueued;
-
- CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION)
- {
- nServices = 0;
- hSocket = hSocketIn;
- nRecvVersion = INIT_PROTO_VERSION;
- nLastSend = 0;
- nLastRecv = 0;
- nSendBytes = 0;
- nRecvBytes = 0;
- nLastSendEmpty = GetTime();
- nTimeConnected = GetTime();
- addr = addrIn;
- addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
- nVersion = 0;
- strSubVer = "";
- fOneShot = false;
- fClient = false; // set by version message
- fInbound = fInboundIn;
- fNetworkNode = false;
- fSuccessfullyConnected = false;
- fDisconnect = false;
- nRefCount = 0;
- nSendSize = 0;
- nSendOffset = 0;
- hashContinue = 0;
- pindexLastGetBlocksBegin = 0;
- hashLastGetBlocksEnd = 0;
- nStartingHeight = -1;
- fStartSync = false;
- fGetAddr = false;
- fRelayTxes = false;
- setInventoryKnown.max_size(SendBufferSize() / 1000);
- pfilter = new CBloomFilter();
- nPingNonceSent = 0;
- nPingUsecStart = 0;
- nPingUsecTime = 0;
- fPingQueued = false;
-
- {
- LOCK(cs_nLastNodeId);
- id = nLastNodeId++;
- }
+private:
+ const NodeId id;
+ const uint64_t nLocalHostNonce;
+ // Services offered to this peer
+ const ServiceFlags nLocalServices;
+ const int nMyStartingHeight;
+ int nSendVersion;
+ std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
+
+ mutable CCriticalSection cs_addrName;
+ std::string addrName;
- // Be shy and don't send version until we hear
- if (hSocket != INVALID_SOCKET && !fInbound)
- PushVersion();
+ // Our address, as reported by the peer
+ CService addrLocal;
+ mutable CCriticalSection cs_addrLocal;
+public:
- GetNodeSignals().InitializeNode(GetId(), this);
+ NodeId GetId() const {
+ return id;
}
- ~CNode()
- {
- if (hSocket != INVALID_SOCKET)
- {
- closesocket(hSocket);
- hSocket = INVALID_SOCKET;
- }
- if (pfilter)
- delete pfilter;
- GetNodeSignals().FinalizeNode(GetId());
+ uint64_t GetLocalNonce() const {
+ return nLocalHostNonce;
}
-private:
- // Network usage totals
- static CCriticalSection cs_totalBytesRecv;
- static CCriticalSection cs_totalBytesSent;
- static uint64_t nTotalBytesRecv;
- static uint64_t nTotalBytesSent;
-
- CNode(const CNode&);
- void operator=(const CNode&);
-
-public:
-
- NodeId GetId() const {
- return id;
+ int GetMyStartingHeight() const {
+ return nMyStartingHeight;
}
- int GetRefCount()
+ int GetRefCount() const
{
assert(nRefCount >= 0);
return nRefCount;
}
- // requires LOCK(cs_vRecvMsg)
- unsigned int GetTotalRecvSize()
- {
- unsigned int total = 0;
- BOOST_FOREACH(const CNetMessage &msg, vRecvMsg)
- total += msg.vRecv.size() + 24;
- return total;
- }
-
- // 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)
{
nRecvVersion = nVersionIn;
- BOOST_FOREACH(CNetMessage &msg, vRecvMsg)
- msg.SetVersion(nVersionIn);
}
+ int GetRecvVersion() const
+ {
+ return nRecvVersion;
+ }
+ void SetSendVersion(int nVersionIn);
+ int GetSendVersion() const;
+
+ CService GetAddrLocal() const;
+ //! May not be called more than once
+ void SetAddrLocal(const CService& addrLocalIn);
CNode* AddRef()
{
@@ -388,18 +788,23 @@ public:
- void AddAddressKnown(const CAddress& addr)
+ void AddAddressKnown(const CAddress& _addr)
{
- setAddrKnown.insert(addr);
+ addrKnown.insert(_addr.GetKey());
}
- void PushAddress(const CAddress& addr)
+ void PushAddress(const CAddress& _addr, FastRandomContext &insecure_rand)
{
// Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added
// after addresses were pushed.
- if (addr.IsValid() && !setAddrKnown.count(addr))
- vAddrToSend.push_back(addr);
+ if (_addr.IsValid() && !addrKnown.contains(_addr.GetKey())) {
+ if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
+ vAddrToSend[insecure_rand.randrange(vAddrToSend.size())] = _addr;
+ } else {
+ vAddrToSend.push_back(_addr);
+ }
+ }
}
@@ -407,320 +812,49 @@ public:
{
{
LOCK(cs_inventory);
- setInventoryKnown.insert(inv);
+ filterInventoryKnown.insert(inv.hash);
}
}
void PushInventory(const CInv& inv)
{
- {
- LOCK(cs_inventory);
- if (!setInventoryKnown.count(inv))
- vInventoryToSend.push_back(inv);
+ LOCK(cs_inventory);
+ if (inv.type == MSG_TX) {
+ if (!filterInventoryKnown.contains(inv.hash)) {
+ setInventoryTxToSend.insert(inv.hash);
+ }
+ } else if (inv.type == MSG_BLOCK) {
+ vInventoryBlockToSend.push_back(inv.hash);
}
}
- void AskFor(const CInv& inv)
+ void PushBlockHash(const uint256 &hash)
{
- // We're using mapAskFor as a priority queue,
- // the key is the earliest time the request can be sent
- int64_t nRequestTime;
- limitedmap<CInv, int64_t>::const_iterator it = mapAlreadyAskedFor.find(inv);
- if (it != mapAlreadyAskedFor.end())
- nRequestTime = it->second;
- else
- nRequestTime = 0;
- LogPrint("net", "askfor %s %d (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str());
-
- // Make sure not to reuse time indexes to keep things in the same order
- int64_t nNow = GetTimeMicros() - 1000000;
- static int64_t nLastTime;
- ++nLastTime;
- nNow = std::max(nNow, nLastTime);
- nLastTime = nNow;
-
- // Each retry is 2 minutes after the last
- nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
- if (it != mapAlreadyAskedFor.end())
- mapAlreadyAskedFor.update(it, nRequestTime);
- else
- mapAlreadyAskedFor.insert(std::make_pair(inv, nRequestTime));
- mapAskFor.insert(std::make_pair(nRequestTime, inv));
+ LOCK(cs_inventory);
+ vBlockHashesToAnnounce.push_back(hash);
}
+ void AskFor(const CInv& inv);
-
- // TODO: Document the postcondition of this function. Is cs_vSend locked?
- void BeginMessage(const char* pszCommand) EXCLUSIVE_LOCK_FUNCTION(cs_vSend)
- {
- ENTER_CRITICAL_SECTION(cs_vSend);
- assert(ssSend.size() == 0);
- ssSend << CMessageHeader(pszCommand, 0);
- LogPrint("net", "sending: %s ", pszCommand);
- }
-
- // TODO: Document the precondition of this function. Is cs_vSend locked?
- void AbortMessage() UNLOCK_FUNCTION(cs_vSend)
- {
- ssSend.clear();
-
- LEAVE_CRITICAL_SECTION(cs_vSend);
-
- LogPrint("net", "(aborted)\n");
- }
-
- // TODO: Document the precondition of this function. Is cs_vSend locked?
- void EndMessage() UNLOCK_FUNCTION(cs_vSend)
- {
- // The -*messagestest options are intentionally not documented in the help message,
- // since they are only used during development to debug the networking code and are
- // not intended for end-users.
- if (mapArgs.count("-dropmessagestest") && GetRand(GetArg("-dropmessagestest", 2)) == 0)
- {
- LogPrint("net", "dropmessages DROPPING SEND MESSAGE\n");
- AbortMessage();
- return;
- }
- if (mapArgs.count("-fuzzmessagestest"))
- Fuzz(GetArg("-fuzzmessagestest", 10));
-
- if (ssSend.size() == 0)
- return;
-
- // Set the size
- unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE;
- memcpy((char*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], &nSize, sizeof(nSize));
-
- // Set the checksum
- uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end());
- unsigned int nChecksum = 0;
- memcpy(&nChecksum, &hash, sizeof(nChecksum));
- assert(ssSend.size () >= CMessageHeader::CHECKSUM_OFFSET + sizeof(nChecksum));
- memcpy((char*)&ssSend[CMessageHeader::CHECKSUM_OFFSET], &nChecksum, sizeof(nChecksum));
-
- LogPrint("net", "(%d bytes)\n", nSize);
-
- std::deque<CSerializeData>::iterator it = vSendMsg.insert(vSendMsg.end(), CSerializeData());
- ssSend.GetAndClear(*it);
- nSendSize += (*it).size();
-
- // If write queue empty, attempt "optimistic write"
- if (it == vSendMsg.begin())
- SocketSendData(this);
-
- LEAVE_CRITICAL_SECTION(cs_vSend);
- }
-
- void PushVersion();
-
-
- void PushMessage(const char* pszCommand)
- {
- try
- {
- BeginMessage(pszCommand);
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1>
- void PushMessage(const char* pszCommand, const T1& a1)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4 << a5;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4 << a5 << a6;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9>
- void PushMessage(const char* pszCommand, const T1& a1, const T2& a2, const T3& a3, const T4& a4, const T5& a5, const T6& a6, const T7& a7, const T8& a8, const T9& a9)
- {
- try
- {
- BeginMessage(pszCommand);
- ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9;
- EndMessage();
- }
- catch (...)
- {
- AbortMessage();
- throw;
- }
- }
-
- bool IsSubscribed(unsigned int nChannel);
- void Subscribe(unsigned int nChannel, unsigned int nHops=0);
- void CancelSubscribe(unsigned int nChannel);
void CloseSocketDisconnect();
- void Cleanup();
-
- // 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 Ban(const CNetAddr &ip);
void copyStats(CNodeStats &stats);
- // Network stats
- static void RecordBytesRecv(uint64_t bytes);
- static void RecordBytesSent(uint64_t bytes);
+ ServiceFlags GetLocalServices() const
+ {
+ return nLocalServices;
+ }
- static uint64_t GetTotalBytesRecv();
- static uint64_t GetTotalBytesSent();
+ std::string GetAddrName() const;
+ //! Sets the addrName only if it was not previously set
+ void MaybeSetAddrName(const std::string& addrNameIn);
};
-class CTransaction;
-void RelayTransaction(const CTransaction& tx, const uint256& hash);
-void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss);
-/** 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);
-};
-#endif
+/** Return a timestamp in the future (in microseconds) for exponentially distributed events. */
+int64_t PoissonNextSend(int64_t nNow, int average_interval_seconds);
+
+#endif // BITCOIN_NET_H