Files
triangles_v5/src/net.h
T
Krystie 8615e6b46d Merge SAMI-PC hd-wallet + process-I2P into v6 master
Merges the HD wallet work and process-based I2P integration from the
SAMI-PC hd-wallet branch into v6 master. Conflict resolution keeps
v6 embedded I2P (CI2PEmbedded) as primary, includes process-I2P
files for reference, preserves FastImportBlockFile() from hd-wallet,
and keeps v6 version numbers (6.0.0) and wAddressStack Qt layout.
2026-06-29 14:52:53 -07:00

616 lines
18 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 <array>
#include <openssl/rand.h>
#ifndef WIN32
#include <arpa/inet.h>
#endif
#include "mruset.h"
#include "netbase.h"
#include "protocol.h"
#include "addrman.h"
class CNode;
class CBlockIndex;
bool IsInitialBlockDownload();
void ThreadForkDetector(void*);
extern int nBestHeight;
extern int nForkAlertCount;
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 = nullptr);
// Adopt a connected I2P SAM data socket as an inbound peer (called from i2p.cpp).
void AddI2PInboundNode(SOCKET hSocket, const CAddress& addr);
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 = nullptr);
bool IsReachable(const CNetAddr &addr);
void SetReachable(enum Network net, bool fFlag = true);
CAddress GetLocalAddress(const CNetAddr *paddrPeer = nullptr);
enum
{
MSG_TX = 1,
MSG_BLOCK,
};
/** 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 std::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:
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)
// Option C: peer reliability scoring. Higher = more reliable.
// Starts at 100 (neutral), grows with successful block delivery, shrinks with
// disconnects and unreachable-on-connect. Used by sync manager to prefer
// reliable peers for header/block requests and to demote flaky ones.
int nReliabilityScore = 100;
int nDisconnectCount = 0; // disconnects since startup
int nConnectFailures = 0; // host-unreachable on connect attempts
int64_t nLastDisconnectTime = 0; // for flapping detection (many disconnects in short window)
// 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;
int64_t nLastGetaddrTrigger; // last time we sent this peer a discovery round (getaddr+getseederlist+getwalletaddr)
int nSignedPeerBonus; // +N reputation when peer completed walletaddr handshake (signed identity)
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;
nLastGetaddrTrigger = 0;
nSignedPeerBonus = 0;
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({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, typename... Args>
void PushMessage(const char* pszCommand, const T1& a1, const Args&... args)
{
try
{
BeginMessage(pszCommand);
ssSend << a1;
using swallow = int[];
(void)swallow{0, ((void)(ssSend << args), 0)...};
EndMessage();
}
catch (...)
{
AbortMessage();
throw;
}
}
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();
// Option C: recompute reliability score from current counters.
// Call this periodically (e.g. in sync manager tick) to apply the
// flapping penalty (5+ disconnects in 5min = extra 50 penalty).
int RecomputeReliabilityScore();
// 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