Files
triangles_v5/src/net.h
T
sami7777 aef95bdf78 Bump version to v5.9.3 and add RPC command reference
Pairs net.h heartbeat-throttle field with the IBD header-sync fix
in 2a484e4, and ships a full RPC reference doc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:16:19 -07:00

789 lines
22 KiB
C++

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2012 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef TRIANGLES_NET_H
#define TRIANGLES_NET_H
#include <deque>
#include <boost/array.hpp>
#include <openssl/rand.h>
#ifndef WIN32
#include <arpa/inet.h>
#endif
#include "mruset.h"
#include "netbase.h"
#include "protocol.h"
#include "addrman.h"
class CRequestTracker;
class CNode;
class CBlockIndex;
bool IsInitialBlockDownload();
extern int nBestHeight;
inline unsigned int ReceiveFloodSize() { return 50 * 1024 * 1024; } // 50 MB (reduced for Tor-only network)
inline unsigned int SendBufferSize() { return 32 * 1024 * 1024; } // 32 MB
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();
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
void StartNode(void* parg);
void StartTor(void* parg);
bool StopNode();
void SocketSendData(CNode *pnode);
enum
{
LOCAL_NONE, // unknown
LOCAL_IF, // address a local interface listens on
LOCAL_BIND, // address explicit bound to
LOCAL_UPNP, // address reported by UPnP
LOCAL_IRC, // address reported by IRC (deprecated)
LOCAL_HTTP, // address reported by whatismyip.com and similar
LOCAL_MANUAL, // address explicitly specified (-externalip=)
LOCAL_MAX
};
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 SeenLocal(const CService& addr);
bool IsLocal(const CService& addr);
bool GetLocal(CService &addr, const CNetAddr *paddrPeer = NULL);
bool IsReachable(const CNetAddr &addr);
void SetReachable(enum Network net, bool fFlag = true);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
enum
{
MSG_TX = 1,
MSG_BLOCK,
};
class CRequestTracker
{
public:
void (*fn)(void*, CDataStream&);
void* param1;
explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL)
{
fn = fnIn;
param1 = param1In;
}
bool IsNull()
{
return fn == NULL;
}
};
/** Thread types */
enum threadId
{
THREAD_SOCKETHANDLER,
THREAD_OPENCONNECTIONS,
THREAD_MESSAGEHANDLER,
THREAD_RPCLISTENER,
THREAD_UPNP,
THREAD_HTTPSEED,
THREAD_ADDEDCONNECTIONS,
THREAD_DUMPADDRESS,
THREAD_RPCHANDLER,
THREAD_STAKE_MINER,
THREAD_MAX
};
extern bool fClient;
extern bool fDiscover;
extern bool fUseUPnP;
extern uint64_t nLocalServices;
extern uint64_t nLocalHostNonce;
extern CAddress addrSeenByPeer;
extern boost::array<int, THREAD_MAX> vnThreadsRunning;
extern CAddrMan addrman;
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 std::map<CInv, int64_t> mapAlreadyAskedFor;
class CNodeStats
{
public:
uint64_t nServices;
int64_t nLastSend;
int64_t nLastRecv;
uint64_t nSendBytes;
uint64_t nRecvBytes;
int64_t nTimeConnected;
std::string addrName;
int nVersion;
std::string strSubVer;
bool fInbound;
int nStartingHeight;
int nMisbehavior;
int64_t nPingUsecTime;
int nBlocksDelivered;
int64_t nAvgBlockLatencyUs;
};
class CNetMessage {
public:
bool in_data; // parsing header (false) or data (true)
CDataStream hdrbuf; // partially received header
CMessageHeader hdr; // complete header
unsigned int nHdrPos;
CDataStream vRecv; // received message data
unsigned int nDataPos;
int64_t nTime; // time (in microseconds) of message receipt.
CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) {
hdrbuf.resize(24);
in_data = false;
nHdrPos = 0;
nDataPos = 0;
nTime = 0;
}
bool complete() const
{
if (!in_data)
return false;
return (hdr.nMessageSize == nDataPos);
}
void SetVersion(int nVersionIn)
{
hdrbuf.SetVersion(nVersionIn);
vRecv.SetVersion(nVersionIn);
}
int readHeader(const char *pch, unsigned int nBytes);
int readData(const char *pch, unsigned int nBytes);
};
class SecMsgNode
{
public:
SecMsgNode()
{
lastSeen = 0;
lastMatched = 0;
ignoreUntil = 0;
nWakeCounter = 0;
nPeerId = 0;
fEnabled = false;
};
~SecMsgNode() {};
int64_t lastSeen;
int64_t lastMatched;
int64_t ignoreUntil;
uint32_t nWakeCounter;
uint32_t nPeerId;
bool fEnabled;
};
/** Information about a peer */
class CNode
{
public:
// socket
uint64_t nServices;
SOCKET hSocket;
CDataStream ssSend;
size_t nSendSize; // total size of all vSendMsg entries
size_t nSendOffset; // offset inside the first vSendMsg already sent
std::deque<CSerializeData> vSendMsg;
CCriticalSection cs_vSend;
std::deque<CNetMessage> vRecvMsg;
CCriticalSection cs_vRecvMsg;
int nRecvVersion;
int64_t nLastSend;
int64_t nLastRecv;
uint64_t nSendBytes;
uint64_t nRecvBytes;
int64_t nLastSendEmpty;
int64_t nTimeConnected;
CAddress addr;
std::string addrName;
CService addrLocal;
int nVersion;
std::string strSubVer;
bool fOneShot;
bool fClient;
bool fInbound;
bool fNetworkNode;
bool fSuccessfullyConnected;
bool fDisconnect;
bool fPreferHeaders; // peer requested block announcements via headers (sendheaders)
bool fSendCmpct; // peer supports compact block relay (sendcmpct)
CSemaphoreGrant grantOutbound;
int nRefCount;
protected:
// 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;
int nMisbehavior;
public:
std::map<uint256, CRequestTracker> mapRequests;
CCriticalSection cs_mapRequests;
uint256 hashContinue;
CBlockIndex* pindexLastGetBlocksBegin;
uint256 hashLastGetBlocksEnd;
CBlockIndex* pindexLastGetHeadersBegin;
uint256 hashLastGetHeadersEnd;
int nStartingHeight;
int64_t nLastTipCheck; // last time we asked this peer for chain tip
int64_t nLastIbdHeaderRequest; // last time we sent IBD-mode getheaders to this peer (heartbeat throttle)
int64_t nAvgBlockLatencyUs; // rolling average block delivery latency (microseconds)
int nBlocksDelivered; // count of blocks delivered by this peer
int nBestKnownHeight; // highest block height known to this peer (updated from inv/block msgs)
int nIncompatibleGetblocks; // count of getblocks with no common blocks (fork detection)
// BIP 31 ping/pong latency tracking
uint64_t nPingNonceSent; // nonce of last ping sent (0 = no outstanding ping)
int64_t nPingUsecStart; // microsecond timestamp when last ping was sent
int64_t nPingUsecTime; // last measured round-trip time (microseconds), 0 = unknown
int nPingRetryCount; // consecutive pings without pong response
// flood relay
std::vector<CAddress> vAddrToSend;
mruset<CAddress> setAddrKnown;
bool fGetAddr;
std::set<uint256> setKnown;
uint256 hashCheckpointKnown; // triangles: known sent sync-checkpoint
// inventory based relay
mruset<CInv> setInventoryKnown;
std::vector<CInv> vInventoryToSend;
CCriticalSection cs_inventory;
std::multimap<int64_t, CInv> mapAskFor;
SecMsgNode smsgData;
CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000)
{
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;
fPreferHeaders = false;
fSendCmpct = false;
nRefCount = 0;
nSendSize = 0;
nSendOffset = 0;
hashContinue = 0;
pindexLastGetBlocksBegin = 0;
hashLastGetBlocksEnd = 0;
pindexLastGetHeadersBegin = 0;
hashLastGetHeadersEnd = 0;
nStartingHeight = -1;
nLastTipCheck = 0;
nLastIbdHeaderRequest = 0;
nAvgBlockLatencyUs = 0;
nBlocksDelivered = 0;
nBestKnownHeight = -1;
nIncompatibleGetblocks = 0;
nPingNonceSent = 0;
nPingUsecStart = 0;
nPingUsecTime = 0;
nPingRetryCount = 0;
fGetAddr = false;
nMisbehavior = 0;
hashCheckpointKnown = 0;
setInventoryKnown.max_size(SendBufferSize() / 1000);
// Be shy and don't send version until we hear
if (hSocket != INVALID_SOCKET && !fInbound)
{
printf("CNode(): pfrom-addr %s\n", addrName.c_str());
PushVersion();
}
}
~CNode()
{
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
hSocket = INVALID_SOCKET;
}
}
private:
CNode(const CNode&);
void operator=(const CNode&);
public:
int GetRefCount()
{
assert(nRefCount >= 0);
return nRefCount;
}
// requires LOCK(cs_vRecvMsg)
unsigned int GetTotalRecvSize()
{
unsigned int total = 0;
for (const CNetMessage &msg : vRecvMsg)
total += msg.vRecv.size() + 24;
return total;
}
// requires LOCK(cs_vRecvMsg)
bool ReceiveMsgBytes(const char *pch, unsigned int nBytes);
// requires LOCK(cs_vRecvMsg)
void SetRecvVersion(int nVersionIn)
{
nRecvVersion = nVersionIn;
for (CNetMessage &msg : vRecvMsg)
msg.SetVersion(nVersionIn);
}
CNode* AddRef()
{
nRefCount++;
return this;
}
void Release()
{
nRefCount--;
}
void AddAddressKnown(const CAddress& addr)
{
setAddrKnown.insert(addr);
}
void PushAddress(const CAddress& addr)
{
// 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);
}
void AddInventoryKnown(const CInv& inv)
{
{
LOCK(cs_inventory);
setInventoryKnown.insert(inv);
}
}
void PushInventory(const CInv& inv)
{
{
LOCK(cs_inventory);
if (!setInventoryKnown.count(inv))
vInventoryToSend.push_back(inv);
}
}
void AskFor(const CInv& inv)
{
// We're using mapAskFor as a priority queue,
// the key is the earliest time the request can be sent
int64_t& nRequestTime = mapAlreadyAskedFor[inv];
if (fDebugNet)
printf("askfor %s %" PRId64 " (%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 = (GetTime() - 1) * 1000000;
static int64_t nLastTime;
++nLastTime;
nNow = std::max(nNow, nLastTime);
nLastTime = nNow;
// During IBD, request immediately (no 2-minute retry delay)
// Normal operation: each retry is 2 minutes after the last
if (nRequestTime > 0 && IsInitialBlockDownload())
nRequestTime = nNow;
else
nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow);
mapAskFor.insert(std::make_pair(nRequestTime, inv));
}
void BeginMessage(const char* pszCommand)
{
ENTER_CRITICAL_SECTION(cs_vSend);
assert(ssSend.size() == 0);
ssSend << CMessageHeader(pszCommand, 0);
if (fDebug)
printf("sending: %s ", pszCommand);
}
void AbortMessage()
{
ssSend.clear();
LEAVE_CRITICAL_SECTION(cs_vSend);
if (fDebug)
printf("(aborted)\n");
}
void EndMessage()
{
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
printf("dropmessages DROPPING SEND MESSAGE\n");
AbortMessage();
return;
}
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));
if (fDebug) {
printf("(%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;
}
}
void PushRequest(const char* pszCommand,
void (*fn)(void*, CDataStream&), void* param1)
{
uint256 hashReply;
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
{
LOCK(cs_mapRequests);
mapRequests[hashReply] = CRequestTracker(fn, param1);
}
PushMessage(pszCommand, hashReply);
}
template<typename T1>
void PushRequest(const char* pszCommand, const T1& a1,
void (*fn)(void*, CDataStream&), void* param1)
{
uint256 hashReply;
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
{
LOCK(cs_mapRequests);
mapRequests[hashReply] = CRequestTracker(fn, param1);
}
PushMessage(pszCommand, hashReply, a1);
}
template<typename T1, typename T2>
void PushRequest(const char* pszCommand, const T1& a1, const T2& a2,
void (*fn)(void*, CDataStream&), void* param1)
{
uint256 hashReply;
RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply));
{
LOCK(cs_mapRequests);
mapRequests[hashReply] = CRequestTracker(fn, param1);
}
PushMessage(pszCommand, hashReply, a1, a2);
}
void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd);
void PushGetHeaders(CBlockIndex* pindexBegin, uint256 hashEnd);
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(CNetAddr ip, int64_t banTime);
static bool Unban(CNetAddr ip);
static void GetBanned(std::map<CNetAddr, int64_t>& mapBannedOut);
bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot
void copyStats(CNodeStats &stats);
};
inline void RelayInventory(const CInv& inv)
{
// Put on lists to offer to the other nodes
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
pnode->PushInventory(inv);
}
}
class CTransaction;
void RelayTransaction(const CTransaction& tx, const uint256& hash);
void RelayTransaction(const CTransaction& tx, const uint256& hash, const CDataStream& ss);
#endif