2010-08-29 18:58:15 +02:00
// Copyright (c) 2009-2010 Satoshi Nakamoto
2023-04-25 13:51:26 +02:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2023-01-12 22:26:21 +01:00
// Copyright (c) 2014-2023 The Dash Core developers
2014-12-13 05:09:33 +01:00
// Distributed under the MIT software license, see the accompanying
2012-05-18 16:02:28 +02:00
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2010-08-29 18:58:15 +02:00
2013-05-28 01:55:01 +02:00
# if defined(HAVE_CONFIG_H)
2022-08-02 18:34:58 +02:00
# include <config/bitcoin-config.h>
2013-05-28 01:55:01 +02:00
# endif
2020-03-19 23:46:56 +01:00
# include <net.h>
# include <netmessagemaker.h>
2019-01-21 18:45:59 +01:00
# include <banman.h>
2020-03-19 23:46:56 +01:00
# include <clientversion.h>
2020-11-18 17:13:27 +01:00
# include <compat.h>
2020-03-19 23:46:56 +01:00
# include <consensus/consensus.h>
# include <crypto/sha256.h>
2020-11-18 17:13:27 +01:00
# include <i2p.h>
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
# include <net_permissions.h>
2021-05-02 18:44:17 +02:00
# include <netaddress.h>
2020-03-19 23:46:56 +01:00
# include <netbase.h>
2020-05-12 10:45:06 +02:00
# include <protocol.h>
2019-12-04 13:10:58 +01:00
# include <random.h>
2020-03-19 23:46:56 +01:00
# include <scheduler.h>
# include <ui_interface.h>
2022-10-26 13:25:11 +02:00
# include <util/sock.h>
2021-06-27 08:33:13 +02:00
# include <util/strencodings.h>
2023-08-26 11:50:37 +02:00
# include <util/thread.h>
2023-10-23 17:39:39 +02:00
# include <util/time.h>
2022-03-24 05:13:51 +01:00
# include <util/translation.h>
2023-12-05 09:32:18 +01:00
# include <validation.h> // for fDIP0001ActiveAtTip
2020-03-19 23:46:56 +01:00
2021-10-01 21:19:08 +02:00
# include <masternode/meta.h>
# include <masternode/sync.h>
2021-03-17 23:36:11 +01:00
# include <coinjoin/coinjoin.h>
2020-03-19 23:46:56 +01:00
# include <evo/deterministicmns.h>
2016-12-20 14:27:59 +01:00
2020-12-15 17:22:23 +01:00
# include <statsd_client.h>
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2011-07-02 03:59:37 +02:00
# include <string.h>
2013-04-13 07:13:08 +02:00
# else
2013-07-17 10:51:40 +02:00
# include <fcntl.h>
# endif
2021-03-31 08:37:06 +02:00
# if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
# include <ifaddrs.h>
# endif
2018-09-27 03:54:52 +02:00
# ifdef USE_POLL
# include <poll.h>
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
# include <sys/epoll.h>
# endif
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
# include <sys/event.h>
# endif
2023-04-16 06:56:24 +02:00
# include <algorithm>
2022-10-20 11:50:51 +02:00
# include <cstdint>
2021-01-26 11:14:06 +01:00
# include <functional>
2018-09-27 03:54:52 +02:00
# include <unordered_map>
2014-01-30 10:55:55 +01:00
2015-04-08 20:20:00 +02:00
# include <math.h>
2023-09-10 19:02:53 +02:00
/** Maximum number of block-relay-only anchor connections */
static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2 ;
static_assert ( MAX_BLOCK_RELAY_ONLY_ANCHORS < = static_cast < size_t > ( MAX_BLOCK_RELAY_ONLY_CONNECTIONS ) , " MAX_BLOCK_RELAY_ONLY_ANCHORS must not exceed MAX_BLOCK_RELAY_ONLY_CONNECTIONS. " ) ;
/** Anchor IP address database file name */
const char * const ANCHORS_DATABASE_FILENAME = " anchors.dat " ;
2023-01-15 12:04:56 +01:00
// How often to dump addresses to peers.dat
static constexpr std : : chrono : : minutes DUMP_PEERS_INTERVAL { 15 } ;
2013-04-13 07:13:08 +02:00
2019-09-23 06:41:32 +02:00
/** Number of DNS seeds to query when the number of connections is low. */
static constexpr int DNSSEEDS_TO_QUERY_AT_ONCE = 3 ;
2020-02-11 04:20:21 +01:00
/** How long to delay before querying DNS seeds
2022-10-15 23:51:44 +02:00
*
* If we have more than THRESHOLD entries in addrman , then it ' s likely
* that we got those addresses from having previously connected to the P2P
* network , and that we ' ll be able to successfully reconnect to the P2P
* network via contacting one of them . So if that ' s the case , spend a
* little longer trying to connect to known peers before querying the
* DNS seeds .
2020-02-11 04:20:21 +01:00
*/
2022-10-15 23:51:44 +02:00
static constexpr std : : chrono : : seconds DNSSEEDS_DELAY_FEW_PEERS { 11 } ;
static constexpr std : : chrono : : minutes DNSSEEDS_DELAY_MANY_PEERS { 5 } ;
static constexpr int DNSSEEDS_DELAY_PEER_THRESHOLD = 1000 ; // "many" vs "few" peers
2020-02-11 04:20:21 +01:00
2022-10-15 23:50:01 +02:00
/** The default timeframe for -maxuploadtarget. 1 day. */
static constexpr std : : chrono : : seconds MAX_UPLOAD_TIMEFRAME { 60 * 60 * 24 } ;
2022-07-26 16:02:13 +02:00
// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization.
static constexpr auto FEELER_SLEEP_WINDOW { 1 s } ;
2017-07-17 12:39:12 +02:00
2017-06-26 14:42:46 +02:00
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0 ,
BF_EXPLICIT = ( 1U < < 0 ) ,
BF_REPORT_ERROR = ( 1U < < 1 ) ,
2023-04-17 10:27:07 +02:00
/**
* Do not call AddLocal ( ) for our special addresses , e . g . , for incoming
* Tor connections , to prevent gossiping them over the network .
*/
BF_DONT_ADVERTISE = ( 1U < < 2 ) ,
2017-06-26 14:42:46 +02:00
} ;
2020-04-10 19:51:07 +02:00
# ifndef USE_WAKEUP_PIPE
2018-10-29 21:30:30 +01:00
// The set of sockets cannot be modified while waiting
// The sleep time needs to be small to avoid new sockets stalling
static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50 ;
2020-04-10 19:51:07 +02:00
# else
// select() is woken up through the wakeup pipe whenever a new node is added, so we can wait much longer.
// We are however still somewhat limited in how long we can sleep as there is periodic work (cleanup) to be done in
// the socket handler thread
2020-04-19 14:48:44 +02:00
static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 500 ;
2020-04-10 19:51:07 +02:00
# endif
2018-10-29 21:30:30 +01:00
2019-01-07 15:41:14 +01:00
const std : : string NET_MESSAGE_COMMAND_OTHER = " *other* " ;
2017-06-29 03:51:10 +02:00
2017-08-17 20:37:22 +02:00
constexpr const CConnman : : CFullyConnectedOnly CConnman : : FullyConnectedOnly ;
constexpr const CConnman : : CAllNodes CConnman : : AllNodes ;
2016-09-19 17:05:35 +02:00
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL ; // SHA256("netgroup")[0:8]
2016-11-03 10:45:11 +01:00
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL ; // SHA256("localhostnonce")[0:8]
2023-06-03 08:28:46 +02:00
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL ; // SHA256("addrcache")[0:8]
2010-08-29 18:58:15 +02:00
//
// Global state variables
//
2012-05-24 19:02:21 +02:00
bool fDiscover = true ;
2014-05-29 12:33:17 +02:00
bool fListen = true ;
2022-01-20 14:57:55 +01:00
Mutex g_maplocalhost_mutex ;
std : : map < CNetAddr , LocalServiceInfo > mapLocalHost GUARDED_BY ( g_maplocalhost_mutex ) ;
static bool vfLimited [ NET_MAX ] GUARDED_BY ( g_maplocalhost_mutex ) = { } ;
2015-07-31 18:05:42 +02:00
std : : string strSubVersion ;
2010-08-29 18:58:15 +02:00
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
2017-07-21 11:35:19 +02:00
void CConnman : : AddOneShot ( const std : : string & strDest )
2012-04-24 02:15:00 +02:00
{
LOCK ( cs_vOneShots ) ;
vOneShots . push_back ( strDest ) ;
}
2022-10-20 11:50:51 +02:00
uint16_t GetListenPort ( )
2011-04-21 16:45:08 +02:00
{
2021-03-01 21:35:28 +01:00
return static_cast < uint16_t > ( gArgs . GetArg ( " -port " , Params ( ) . GetDefaultPort ( ) ) ) ;
2011-04-21 16:45:08 +02:00
}
2010-08-29 18:58:15 +02:00
2012-02-12 13:45:24 +01:00
// find 'best' local address for a particular peer
2012-05-10 20:35:13 +02:00
bool GetLocal ( CService & addr , const CNetAddr * paddrPeer )
2012-02-12 13:45:24 +01:00
{
2014-05-29 12:33:17 +02:00
if ( ! fListen )
2012-02-12 13:45:24 +01:00
return false ;
2010-08-29 18:58:15 +02:00
2012-05-13 00:41:24 +02:00
int nBestScore = - 1 ;
2012-02-12 13:45:24 +01:00
int nBestReachability = - 1 ;
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2017-11-30 23:09:44 +01:00
for ( const auto & entry : mapLocalHost )
2012-02-12 13:45:24 +01:00
{
2017-11-30 23:09:44 +01:00
int nScore = entry . second . nScore ;
int nReachability = entry . first . GetReachabilityFrom ( paddrPeer ) ;
2012-05-13 00:41:24 +02:00
if ( nReachability > nBestReachability | | ( nReachability = = nBestReachability & & nScore > nBestScore ) )
2012-02-12 13:45:24 +01:00
{
2017-11-30 23:09:44 +01:00
addr = CService ( entry . first , entry . second . nPort ) ;
2012-02-12 13:45:24 +01:00
nBestReachability = nReachability ;
2012-05-13 00:41:24 +02:00
nBestScore = nScore ;
2012-02-12 13:45:24 +01:00
}
}
}
2012-05-13 00:41:24 +02:00
return nBestScore > = 0 ;
2012-02-12 13:45:24 +01:00
}
2010-08-29 18:58:15 +02:00
2023-09-10 16:10:08 +02:00
//! Convert the serialized seeds into usable address objects.
static std : : vector < CAddress > ConvertSeeds ( const std : : vector < uint8_t > & vSeedsIn )
2015-01-24 05:40:50 +01:00
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
// Seed nodes are given a random 'last seen time' of between one and two
// weeks ago.
const int64_t nOneWeek = 7 * 24 * 60 * 60 ;
std : : vector < CAddress > vSeedsOut ;
2018-12-13 13:43:12 +01:00
FastRandomContext rng ;
2023-09-10 16:10:08 +02:00
CDataStream s ( vSeedsIn , SER_NETWORK , PROTOCOL_VERSION | ADDRV2_FORMAT ) ;
while ( ! s . eof ( ) ) {
CService endpoint ;
s > > endpoint ;
CAddress addr { endpoint , GetDesirableServiceFlags ( NODE_NONE ) } ;
2018-12-13 13:43:12 +01:00
addr . nTime = GetTime ( ) - rng . randrange ( nOneWeek ) - nOneWeek ;
2023-09-10 16:10:08 +02:00
LogPrint ( BCLog : : NET , " Added hardcoded seed: %s \n " , addr . ToString ( ) ) ;
2015-01-24 05:40:50 +01:00
vSeedsOut . push_back ( addr ) ;
}
return vSeedsOut ;
}
2012-02-12 13:45:24 +01:00
// get best local address for a particular peer as a CAddress
2014-07-21 08:32:25 +02:00
// 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.
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
2017-07-21 11:35:19 +02:00
CAddress GetLocalAddress ( const CNetAddr * paddrPeer , ServiceFlags nLocalServices )
2012-02-12 13:45:24 +01:00
{
2017-05-22 12:49:44 +02:00
CAddress ret ( CService ( CNetAddr ( ) , GetListenPort ( ) ) , nLocalServices ) ;
2012-05-10 20:35:13 +02:00
CService addr ;
2012-02-12 13:45:24 +01:00
if ( GetLocal ( addr , paddrPeer ) )
{
2017-07-05 05:45:23 +02:00
ret = CAddress ( addr , nLocalServices ) ;
2012-02-12 13:45:24 +01:00
}
2014-07-21 08:32:25 +02:00
ret . nTime = GetAdjustedTime ( ) ;
2012-02-12 13:45:24 +01:00
return ret ;
}
2010-08-29 18:58:15 +02:00
2018-05-04 22:42:39 +02:00
static int GetnScore ( const CService & addr )
2012-02-12 13:45:24 +01:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2021-09-17 14:25:10 +02:00
const auto it = mapLocalHost . find ( addr ) ;
return ( it ! = mapLocalHost . end ( ) ) ? it - > second . nScore : 0 ;
2014-07-21 08:32:25 +02:00
}
// Is our peer's addrLocal potentially useful as an external IP source?
bool IsPeerAddrLocalGood ( CNode * pnode )
{
2017-02-11 00:53:31 +01:00
CService addrLocal = pnode - > GetAddrLocal ( ) ;
return fDiscover & & pnode - > addr . IsRoutable ( ) & & addrLocal . IsRoutable ( ) & &
2019-01-14 14:22:47 +01:00
IsReachable ( addrLocal . GetNetwork ( ) ) ;
2014-07-21 08:32:25 +02:00
}
2021-02-19 12:58:30 +01:00
std : : optional < CAddress > GetLocalAddrForPeer ( CNode * pnode )
2014-07-21 08:32:25 +02:00
{
2021-02-19 12:58:30 +01:00
CAddress addrLocal = GetLocalAddress ( & pnode - > addr , pnode - > GetLocalServices ( ) ) ;
if ( gArgs . GetBoolArg ( " -addrmantest " , false ) ) {
// use IPv4 loopback during addrmantest
addrLocal = CAddress ( CService ( LookupNumeric ( " 127.0.0.1 " , GetListenPort ( ) ) ) , 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.
FastRandomContext rng ;
if ( IsPeerAddrLocalGood ( pnode ) & & ( ! addrLocal . IsRoutable ( ) | |
rng . randbits ( ( GetnScore ( addrLocal ) > LOCAL_MANUAL ) ? 3 : 1 ) = = 0 ) )
2012-02-12 13:45:24 +01:00
{
2021-02-19 12:58:30 +01:00
addrLocal . SetIP ( pnode - > GetAddrLocal ( ) ) ;
}
if ( addrLocal . IsRoutable ( ) | | gArgs . GetBoolArg ( " -addrmantest " , false ) )
{
LogPrint ( BCLog : : NET , " Advertising address %s to peer=%d \n " , addrLocal . ToString ( ) , pnode - > GetId ( ) ) ;
return addrLocal ;
2012-02-12 13:45:24 +01:00
}
2021-02-19 12:58:30 +01:00
// Address is unroutable. Don't advertise.
return std : : nullopt ;
2012-02-12 13:45:24 +01:00
}
// learn a new local address
2012-05-10 20:35:13 +02:00
bool AddLocal ( const CService & addr , int nScore )
2012-02-12 13:45:24 +01:00
{
2018-07-12 11:04:42 +02:00
if ( ! addr . IsRoutable ( ) & & Params ( ) . RequireRoutableExternalIP ( ) )
2012-02-12 13:45:24 +01:00
return false ;
2012-05-24 19:02:21 +02:00
if ( ! fDiscover & & nScore < LOCAL_MANUAL )
2012-05-13 14:11:53 +02:00
return false ;
2019-01-14 14:22:47 +01:00
if ( ! IsReachable ( addr ) )
2012-05-13 15:11:51 +02:00
return false ;
2014-01-16 16:15:27 +01:00
LogPrintf ( " AddLocal(%s,%i) \n " , addr . ToString ( ) , nScore ) ;
2012-02-12 13:45:24 +01:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2021-09-17 14:25:10 +02:00
const auto [ it , is_newly_added ] = mapLocalHost . emplace ( addr , LocalServiceInfo ( ) ) ;
LocalServiceInfo & info = it - > second ;
if ( is_newly_added | | nScore > = info . nScore ) {
info . nScore = nScore + ( is_newly_added ? 0 : 1 ) ;
2012-08-29 02:33:25 +02:00
info . nPort = addr . GetPort ( ) ;
2012-05-13 00:41:24 +02:00
}
2012-02-12 13:45:24 +01:00
}
return true ;
}
2012-05-13 01:26:14 +02:00
bool AddLocal ( const CNetAddr & addr , int nScore )
2012-05-10 20:35:13 +02:00
{
2012-05-13 01:26:14 +02:00
return AddLocal ( CService ( addr , GetListenPort ( ) ) , nScore ) ;
2012-05-10 20:35:13 +02:00
}
2021-06-29 12:46:52 +02:00
void RemoveLocal ( const CService & addr )
2015-09-08 17:48:45 +02:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2015-09-08 17:48:45 +02:00
LogPrintf ( " RemoveLocal(%s) \n " , addr . ToString ( ) ) ;
mapLocalHost . erase ( addr ) ;
}
2019-01-14 14:22:47 +01:00
void SetReachable ( enum Network net , bool reachable )
2012-05-04 16:46:22 +02:00
{
2017-06-24 12:16:41 +02:00
if ( net = = NET_UNROUTABLE | | net = = NET_INTERNAL )
2012-05-14 17:15:58 +02:00
return ;
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2019-01-14 14:22:47 +01:00
vfLimited [ net ] = ! reachable ;
2012-05-04 16:46:22 +02:00
}
2019-01-14 14:22:47 +01:00
bool IsReachable ( enum Network net )
2012-05-04 16:46:22 +02:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2019-01-14 14:22:47 +01:00
return ! vfLimited [ net ] ;
2012-05-14 17:15:58 +02:00
}
2019-01-14 14:22:47 +01:00
bool IsReachable ( const CNetAddr & addr )
2012-05-14 17:15:58 +02:00
{
2019-01-14 14:22:47 +01:00
return IsReachable ( addr . GetNetwork ( ) ) ;
2012-05-04 16:46:22 +02:00
}
/** vote for a local address */
2012-05-10 20:35:13 +02:00
bool SeenLocal ( const CService & addr )
2012-02-12 13:45:24 +01:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2021-09-17 14:25:10 +02:00
const auto it = mapLocalHost . find ( addr ) ;
if ( it = = mapLocalHost . end ( ) ) return false ;
+ + it - > second . nScore ;
2012-02-12 13:45:24 +01:00
return true ;
}
2014-07-21 08:32:25 +02:00
2012-05-04 16:46:22 +02:00
/** check whether a given address is potentially local */
2012-05-10 20:35:13 +02:00
bool IsLocal ( const CService & addr )
2012-02-12 13:45:24 +01:00
{
2022-01-20 14:57:55 +01:00
LOCK ( g_maplocalhost_mutex ) ;
2012-02-12 13:45:24 +01:00
return mapLocalHost . count ( addr ) > 0 ;
}
2010-08-29 18:58:15 +02:00
2020-04-21 18:21:19 +02:00
CNode * CConnman : : FindNode ( const CNetAddr & ip , bool fExcludeDisconnecting )
2010-08-29 18:58:15 +02:00
{
2013-04-04 11:30:55 +02:00
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( CNode * pnode : vNodes ) {
2020-04-21 18:21:19 +02:00
if ( fExcludeDisconnecting & & pnode - > fDisconnect ) {
continue ;
}
2018-02-07 22:15:16 +01:00
if ( static_cast < CNetAddr > ( pnode - > addr ) = = ip ) {
2017-09-21 01:32:56 +02:00
return pnode ;
}
}
2019-08-06 05:08:33 +02:00
return nullptr ;
2010-08-29 18:58:15 +02:00
}
2020-04-21 18:21:19 +02:00
CNode * CConnman : : FindNode ( const CSubNet & subNet , bool fExcludeDisconnecting )
2015-05-25 20:03:51 +02:00
{
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( CNode * pnode : vNodes ) {
2020-04-21 18:21:19 +02:00
if ( fExcludeDisconnecting & & pnode - > fDisconnect ) {
continue ;
}
2018-02-07 22:15:16 +01:00
if ( subNet . Match ( static_cast < CNetAddr > ( pnode - > addr ) ) ) {
2017-09-21 01:32:56 +02:00
return pnode ;
}
}
2019-08-06 05:08:33 +02:00
return nullptr ;
2015-05-25 20:03:51 +02:00
}
2020-04-21 18:21:19 +02:00
CNode * CConnman : : FindNode ( const std : : string & addrName , bool fExcludeDisconnecting )
2012-04-19 17:38:03 +02:00
{
LOCK ( cs_vNodes ) ;
2019-07-05 09:06:28 +02:00
for ( CNode * pnode : vNodes ) {
2020-04-21 18:21:19 +02:00
if ( fExcludeDisconnecting & & pnode - > fDisconnect ) {
continue ;
}
2017-02-11 00:53:31 +01:00
if ( pnode - > GetAddrName ( ) = = addrName ) {
2017-09-21 01:32:56 +02:00
return pnode ;
2017-02-11 00:53:31 +01:00
}
}
2019-08-06 05:08:33 +02:00
return nullptr ;
2012-04-19 17:38:03 +02:00
}
2020-04-21 18:21:19 +02:00
CNode * CConnman : : FindNode ( const CService & addr , bool fExcludeDisconnecting )
2010-08-29 18:58:15 +02:00
{
2013-04-04 11:30:55 +02:00
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( CNode * pnode : vNodes ) {
2020-04-21 18:21:19 +02:00
if ( fExcludeDisconnecting & & pnode - > fDisconnect ) {
continue ;
}
2018-02-07 22:15:16 +01:00
if ( static_cast < CService > ( pnode - > addr ) = = addr ) {
2017-09-21 01:32:56 +02:00
return pnode ;
}
}
2019-08-06 05:08:33 +02:00
return nullptr ;
2010-08-29 18:58:15 +02:00
}
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
2017-07-21 11:35:19 +02:00
bool CConnman : : CheckIncomingNonce ( uint64_t nonce )
{
LOCK ( cs_vNodes ) ;
2018-09-04 15:36:09 +02:00
for ( const CNode * pnode : vNodes ) {
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
2017-07-21 11:35:19 +02:00
if ( ! pnode - > fSuccessfullyConnected & & ! pnode - > fInbound & & pnode - > GetLocalNonce ( ) = = nonce )
return false ;
}
return true ;
}
2017-06-05 15:39:11 +02:00
/** Get the bind address for a socket as CAddress */
static CAddress GetBindAddress ( SOCKET sock )
{
CAddress addr_bind ;
struct sockaddr_storage sockaddr_bind ;
socklen_t sockaddr_bind_len = sizeof ( sockaddr_bind ) ;
if ( sock ! = INVALID_SOCKET ) {
if ( ! getsockname ( sock , ( struct sockaddr * ) & sockaddr_bind , & sockaddr_bind_len ) ) {
addr_bind . SetSockAddr ( ( const struct sockaddr * ) & sockaddr_bind ) ;
} else {
LogPrint ( BCLog : : NET , " Warning: getsockname failed \n " ) ;
}
}
return addr_bind ;
}
2022-06-19 08:02:28 +02:00
CNode * CConnman : : ConnectNode ( CAddress addrConnect , const char * pszDest , bool fCountFailure , bool manual_connection , bool block_relay_only )
2010-08-29 18:58:15 +02:00
{
2019-08-06 05:08:33 +02:00
if ( pszDest = = nullptr ) {
2018-07-07 23:19:33 +02:00
bool fAllowLocal = Params ( ) . AllowMultiplePorts ( ) & & addrConnect . GetPort ( ) ! = GetListenPort ( ) ;
if ( ! fAllowLocal & & IsLocal ( addrConnect ) ) {
2019-08-06 05:08:33 +02:00
return nullptr ;
2018-07-07 23:19:33 +02:00
}
2010-08-29 18:58:15 +02:00
2012-04-19 17:38:03 +02:00
// Look for an existing connection
2018-02-07 22:15:16 +01:00
CNode * pnode = FindNode ( static_cast < CService > ( addrConnect ) ) ;
2012-04-19 17:38:03 +02:00
if ( pnode )
{
2017-01-24 22:51:22 +01:00
LogPrintf ( " Failed to open new connection, already connected \n " ) ;
2019-08-06 05:08:33 +02:00
return nullptr ;
2012-04-19 17:38:03 +02:00
}
2010-08-29 18:58:15 +02:00
}
/// debug print
2019-09-22 22:48:15 +02:00
if ( fLogIPs ) {
LogPrint ( BCLog : : NET , " trying connection %s lastseen=%.1fhrs \n " ,
pszDest ? pszDest : addrConnect . ToString ( ) ,
pszDest ? 0.0 : ( double ) ( GetAdjustedTime ( ) - addrConnect . nTime ) / 3600.0 ) ;
} else {
LogPrint ( BCLog : : NET , " trying connection lastseen=%.1fhrs \n " ,
pszDest ? 0.0 : ( double ) ( GetAdjustedTime ( ) - addrConnect . nTime ) / 3600.0 ) ;
}
2010-08-29 18:58:15 +02:00
2017-09-28 17:02:53 +02:00
// Resolve
2023-07-14 17:22:44 +02:00
const uint16_t default_port { pszDest ! = nullptr ? Params ( ) . GetDefaultPort ( pszDest ) :
Params ( ) . GetDefaultPort ( ) } ;
2017-09-28 17:02:53 +02:00
if ( pszDest ) {
std : : vector < CService > resolved ;
if ( Lookup ( pszDest , resolved , default_port , fNameLookup & & ! HaveNameProxy ( ) , 256 ) & & ! resolved . empty ( ) ) {
addrConnect = CAddress ( resolved [ GetRand ( resolved . size ( ) ) ] , NODE_NONE ) ;
if ( ! addrConnect . IsValid ( ) ) {
2018-05-02 15:56:30 +02:00
LogPrint ( BCLog : : NET , " Resolver returned invalid address %s for %s \n " , addrConnect . ToString ( ) , pszDest ) ;
2017-09-28 17:02:53 +02:00
return nullptr ;
}
2017-07-14 18:58:57 +02:00
// It is possible that we already have a connection to the IP/port pszDest resolved to.
// In that case, drop the connection that was just created, and return the existing CNode instead.
// Also store the name we used to connect in that CNode, so that future FindNode() calls to that
// name catch this early.
2017-08-29 01:51:56 +02:00
LOCK ( cs_vNodes ) ;
2018-02-07 22:15:16 +01:00
CNode * pnode = FindNode ( static_cast < CService > ( addrConnect ) ) ;
2017-07-14 18:58:57 +02:00
if ( pnode )
{
2017-02-11 00:53:31 +01:00
pnode - > MaybeSetAddrName ( std : : string ( pszDest ) ) ;
2017-01-24 22:51:22 +01:00
LogPrintf ( " Failed to open new connection, already connected \n " ) ;
2019-08-06 05:08:33 +02:00
return nullptr ;
2017-07-14 18:58:57 +02:00
}
}
2017-09-28 17:02:53 +02:00
}
2017-07-14 18:58:57 +02:00
2017-09-28 17:02:53 +02:00
// Connect
bool connected = false ;
2022-10-26 13:25:11 +02:00
std : : unique_ptr < Sock > sock ;
2017-09-28 17:02:53 +02:00
proxyType proxy ;
2020-11-18 17:13:27 +01:00
CAddress addr_bind ;
assert ( ! addr_bind . IsValid ( ) ) ;
2017-09-28 17:02:53 +02:00
if ( addrConnect . IsValid ( ) ) {
bool proxyConnectionFailed = false ;
2020-11-18 17:13:27 +01:00
if ( addrConnect . GetNetwork ( ) = = NET_I2P & & m_i2p_sam_session . get ( ) ! = nullptr ) {
i2p : : Connection conn ;
if ( m_i2p_sam_session - > Connect ( addrConnect , conn , proxyConnectionFailed ) ) {
connected = true ;
2023-07-14 17:11:45 +02:00
sock = std : : move ( conn . sock ) ;
2020-11-18 17:13:27 +01:00
addr_bind = CAddress { conn . me , NODE_NONE } ;
}
} else if ( GetProxy ( addrConnect . GetNetwork ( ) , proxy ) ) {
2022-10-26 13:25:11 +02:00
sock = CreateSock ( proxy . proxy ) ;
if ( ! sock ) {
2017-12-13 05:09:57 +01:00
return nullptr ;
}
2022-10-26 13:25:11 +02:00
connected = ConnectThroughProxy ( proxy , addrConnect . ToStringIP ( ) , addrConnect . GetPort ( ) ,
* sock , nConnectTimeout , proxyConnectionFailed ) ;
2017-12-13 05:09:57 +01:00
} else {
// no proxy needed (none set for target network)
2022-10-26 13:25:11 +02:00
sock = CreateSock ( addrConnect ) ;
if ( ! sock ) {
2017-12-13 05:09:57 +01:00
return nullptr ;
}
2023-07-14 17:11:45 +02:00
connected = ConnectSocketDirectly ( addrConnect , * sock , nConnectTimeout , manual_connection ) ;
2017-12-13 05:09:57 +01:00
}
2017-09-28 17:02:53 +02:00
if ( ! proxyConnectionFailed ) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
// the proxy, mark this as an attempt.
addrman . Attempt ( addrConnect , fCountFailure ) ;
}
} else if ( pszDest & & GetNameProxy ( proxy ) ) {
2022-10-26 13:25:11 +02:00
sock = CreateSock ( proxy . proxy ) ;
if ( ! sock ) {
2017-12-13 05:09:57 +01:00
return nullptr ;
}
2017-09-28 17:02:53 +02:00
std : : string host ;
2021-03-01 21:35:28 +01:00
uint16_t port { default_port } ;
2017-09-28 17:02:53 +02:00
SplitHostPort ( std : : string ( pszDest ) , port , host ) ;
Merge #17754: net: Don't allow resolving of std::string with embedded NUL characters. Add tests.
7a046cdc1423963bdcbcf9bb98560af61fa90b37 tests: Avoid using C-style NUL-terminated strings as arguments (practicalswift)
fefb9165f23fe9d10ad092ec31715f906e0d2ee7 tests: Add tests to make sure lookup methods fail on std::string parameters with embedded NUL characters (practicalswift)
9574de86ad703ad942cdd0eca79f48c0d42b102b net: Avoid using C-style NUL-terminated strings as arguments in the netbase interface (practicalswift)
Pull request description:
Don't allow resolving of `std::string`:s with embedded `NUL` characters.
Avoid using C-style `NUL`-terminated strings as arguments in the `netbase` interface
Add tests.
The only place in where C-style `NUL`-terminated strings are actually needed is here:
```diff
+ if (!ValidAsCString(name)) {
+ return false;
+ }
...
- int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
+ int nErr = getaddrinfo(name.c_str(), nullptr, &aiHint, &aiRes);
if (nErr)
return false;
```
Interface changes:
```diff
-bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
+bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
-bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup);
+bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup);
-bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup);
+bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup);
-bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
+bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
-bool LookupSubNet(const char *pszName, CSubNet& subnet);
+bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet);
-CService LookupNumeric(const char *pszName, int portDefault = 0);
+CService LookupNumeric(const std::string& name, int portDefault = 0);
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
+bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool& outProxyConnectionFailed);
```
It should be noted that the `ConnectThroughProxy` change (from `bool *outProxyConnectionFailed` to `bool& outProxyConnectionFailed`) has nothing to do with `NUL` handling but I thought it was worth doing when touching this file :)
ACKs for top commit:
EthanHeilman:
ACK 7a046cdc1423963bdcbcf9bb98560af61fa90b37
laanwj:
ACK 7a046cdc1423963bdcbcf9bb98560af61fa90b37
Tree-SHA512: 66556e290db996917b54091acd591df221f72230f6b9f6b167b9195ee870ebef6e26f4cda2f6f54d00e1c362e1743bf56785d0de7cae854e6bf7d26f6caccaba
2020-01-22 20:14:12 +01:00
bool proxyConnectionFailed ;
2022-10-26 13:25:11 +02:00
connected = ConnectThroughProxy ( proxy , host , port , * sock , nConnectTimeout ,
proxyConnectionFailed ) ;
2017-09-28 17:02:53 +02:00
}
2017-12-13 05:09:57 +01:00
if ( ! connected ) {
return nullptr ;
2010-08-29 18:58:15 +02:00
}
2014-05-24 11:14:52 +02:00
2017-12-13 05:09:57 +01:00
// Add node
NodeId id = GetNewNodeId ( ) ;
uint64_t nonce = GetDeterministicRandomizer ( RANDOMIZER_ID_LOCALHOSTNONCE ) . Write ( id ) . Finalize ( ) ;
2020-11-18 17:13:27 +01:00
if ( ! addr_bind . IsValid ( ) ) {
addr_bind = GetBindAddress ( sock - > Get ( ) ) ;
}
2023-04-28 08:36:19 +02:00
CNode * pnode = new CNode ( id , nLocalServices , sock - > Release ( ) , addrConnect , CalculateKeyedNetGroup ( addrConnect ) , nonce , addr_bind , pszDest ? pszDest : " " , false , block_relay_only ) ;
2017-12-13 05:09:57 +01:00
pnode - > AddRef ( ) ;
2020-12-15 17:22:23 +01:00
statsClient . inc ( " peers.connect " , 1.0f ) ;
2017-12-13 05:09:57 +01:00
2019-12-04 13:10:58 +01:00
// We're making a new connection, harvest entropy from the time (and our peer count)
RandAddEvent ( ( uint32_t ) id ) ;
2017-12-13 05:09:57 +01:00
return pnode ;
2010-08-29 18:58:15 +02:00
}
2020-04-07 07:00:41 +02:00
void CNode : : CloseSocketDisconnect ( CConnman * connman )
2010-08-29 18:58:15 +02:00
{
2020-04-07 07:00:41 +02:00
AssertLockHeld ( connman - > cs_vNodes ) ;
2010-08-29 18:58:15 +02:00
fDisconnect = true ;
2017-02-10 12:38:45 +01:00
LOCK ( cs_hSocket ) ;
2020-04-07 07:00:41 +02:00
if ( hSocket = = INVALID_SOCKET ) {
return ;
2010-08-29 18:58:15 +02:00
}
2020-04-07 07:00:41 +02:00
2020-04-07 17:52:20 +02:00
fHasRecvData = false ;
fCanSendData = false ;
2020-04-07 07:00:41 +02:00
connman - > mapSocketToNode . erase ( hSocket ) ;
2020-04-07 17:52:20 +02:00
connman - > mapReceivableNodes . erase ( GetId ( ) ) ;
connman - > mapSendableNodes . erase ( GetId ( ) ) ;
{
LOCK ( connman - > cs_mapNodesWithDataToSend ) ;
2020-04-17 23:07:17 +02:00
if ( connman - > mapNodesWithDataToSend . erase ( GetId ( ) ) ! = 0 ) {
// See comment in PushMessage
Release ( ) ;
}
2020-04-07 17:52:20 +02:00
}
2020-04-07 07:00:41 +02:00
2020-04-07 17:58:38 +02:00
connman - > UnregisterEvents ( this ) ;
2020-04-07 07:00:41 +02:00
LogPrint ( BCLog : : NET , " disconnecting peer=%d \n " , id ) ;
CloseSocket ( hSocket ) ;
2020-12-15 17:22:23 +01:00
statsClient . inc ( " peers.disconnect " , 1.0f ) ;
2010-08-29 18:58:15 +02:00
}
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
void CConnman : : AddWhitelistPermissionFlags ( NetPermissionFlags & flags , const CNetAddr & addr ) const {
for ( const auto & subnet : vWhitelistedRange ) {
if ( subnet . m_subnet . Match ( addr ) ) NetPermissions : : AddFlag ( flags , subnet . m_flags ) ;
2014-06-21 13:34:36 +02:00
}
}
2023-04-27 09:21:41 +02:00
bool CNode : : IsBlockRelayOnly ( ) const {
bool ignores_incoming_txs { gArgs . GetBoolArg ( " -blocksonly " , DEFAULT_BLOCKSONLY ) } ;
// Stop processing non-block data early if
// 1) We are in blocks only mode and peer has no relay permission
// 2) This peer is a block-relay-only peer
return ( ignores_incoming_txs & & ! HasPermission ( PF_RELAY ) ) | | ! IsAddrRelayPeer ( ) ;
}
2017-02-11 00:53:31 +01:00
std : : string CNode : : GetAddrName ( ) const {
LOCK ( cs_addrName ) ;
return addrName ;
}
void CNode : : MaybeSetAddrName ( const std : : string & addrNameIn ) {
LOCK ( cs_addrName ) ;
if ( addrName . empty ( ) ) {
addrName = addrNameIn ;
}
}
CService CNode : : GetAddrLocal ( ) const {
LOCK ( cs_addrLocal ) ;
return addrLocal ;
}
void CNode : : SetAddrLocal ( const CService & addrLocalIn ) {
LOCK ( cs_addrLocal ) ;
if ( addrLocal . IsValid ( ) ) {
error ( " Addr local already set for node: %i. Refusing to change from %s to %s " , id , addrLocal . ToString ( ) , addrLocalIn . ToString ( ) ) ;
} else {
addrLocal = addrLocalIn ;
}
}
2019-09-22 22:48:15 +02:00
std : : string CNode : : GetLogString ( ) const
{
return fLogIPs ? addr . ToString ( ) : strprintf ( " %d " , id ) ;
}
2023-04-16 06:56:24 +02:00
Network CNode : : ConnectedThroughNetwork ( ) const
{
return fInbound & & m_inbound_onion ? NET_ONION : addr . GetNetClass ( ) ;
}
2012-06-29 23:24:53 +02:00
# undef X
# define X(name) stats.name = name
2020-01-29 22:55:40 +01:00
void CNode : : copyStats ( CNodeStats & stats , const std : : vector < bool > & m_asmap )
2012-06-29 23:24:53 +02:00
{
2013-11-18 01:25:17 +01:00
stats . nodeid = this - > GetId ( ) ;
2012-06-29 23:24:53 +02:00
X ( nServices ) ;
2017-09-09 09:04:02 +02:00
X ( addr ) ;
2017-06-05 15:39:11 +02:00
X ( addrBind ) ;
2020-12-28 22:52:02 +01:00
stats . m_network = ConnectedThroughNetwork ( ) ;
2021-05-11 17:11:07 +02:00
stats . m_mapped_as = addr . GetMappedAS ( m_asmap ) ;
2023-04-19 16:57:27 +02:00
if ( IsAddrRelayPeer ( ) ) {
2022-06-19 08:02:28 +02:00
LOCK ( m_tx_relay - > cs_filter ) ;
stats . fRelayTxes = m_tx_relay - > fRelayTxes ;
} else {
stats . fRelayTxes = false ;
2017-02-11 00:53:31 +01:00
}
2012-06-29 23:24:53 +02:00
X ( nLastSend ) ;
X ( nLastRecv ) ;
X ( nTimeConnected ) ;
2014-12-15 11:06:15 +01:00
X ( nTimeOffset ) ;
2017-02-11 00:53:31 +01:00
stats . addrName = GetAddrName ( ) ;
2012-06-29 23:24:53 +02:00
X ( nVersion ) ;
2017-02-11 00:53:31 +01:00
{
LOCK ( cs_SubVer ) ;
X ( cleanSubVer ) ;
}
2012-06-29 23:24:53 +02:00
X ( fInbound ) ;
2017-10-14 00:25:16 +02:00
X ( m_manual_connection ) ;
2012-06-29 23:24:53 +02:00
X ( nStartingHeight ) ;
2017-02-11 00:53:31 +01:00
{
LOCK ( cs_vSend ) ;
X ( mapSendBytesPerMsgCmd ) ;
X ( nSendBytes ) ;
}
{
LOCK ( cs_vRecv ) ;
X ( mapRecvBytesPerMsgCmd ) ;
X ( nRecvBytes ) ;
}
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
X ( m_legacyWhitelisted ) ;
X ( m_permissionFlags ) ;
2013-11-15 12:24:34 +01:00
2013-08-22 13:34:33 +02:00
// It is common for nodes with good ping times to suddenly become lagged,
// due to a new block arriving or other large transfer.
// Merely reporting pingtime might fool the caller into thinking the node was still responsive,
// since pingtime does not update until the ping is complete, which might take a while.
// So, if a ping is taking an unusually long time in flight,
// the caller can immediately detect that this is happening.
2013-04-13 07:13:08 +02:00
int64_t nPingUsecWait = 0 ;
2013-08-22 13:34:33 +02:00
if ( ( 0 ! = nPingNonceSent ) & & ( 0 ! = nPingUsecStart ) ) {
nPingUsecWait = GetTimeMicros ( ) - nPingUsecStart ;
}
2013-11-15 12:24:34 +01:00
2015-03-18 00:06:58 +01:00
// Raw ping time is in microseconds, but show it to user as whole seconds (Dash users should be well used to small numbers with many decimal places by now :)
2020-03-03 14:35:01 +01:00
stats . m_ping_usec = nPingUsecTime ;
stats . m_min_ping_usec = nMinPingUsecTime ;
stats . m_ping_wait_usec = nPingUsecWait ;
2013-11-15 12:24:34 +01:00
2013-08-22 07:50:19 +02:00
// Leave string empty if addrLocal invalid (not filled in yet)
2017-02-11 00:53:31 +01:00
CService addrLocalUnlocked = GetAddrLocal ( ) ;
stats . addrLocal = addrLocalUnlocked . IsValid ( ) ? addrLocalUnlocked . ToString ( ) : " " ;
2019-12-06 10:05:58 +01:00
{
LOCK ( cs_mnauth ) ;
X ( verifiedProRegTxHash ) ;
2021-01-22 23:12:22 +01:00
X ( verifiedPubKeyHash ) ;
2019-12-06 10:05:58 +01:00
}
2021-01-14 20:59:18 +01:00
X ( m_masternode_connection ) ;
2012-06-29 23:24:53 +02:00
}
# undef X
2010-08-29 18:58:15 +02:00
2020-11-23 10:25:56 +01:00
bool CNode : : ReceiveMsgBytes ( Span < const uint8_t > msg_bytes , bool & complete )
2012-11-16 01:41:12 +01:00
{
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
2017-07-21 11:35:19 +02:00
complete = false ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
int64_t nTimeMicros = GetTimeMicros ( ) ;
2017-02-11 00:53:31 +01:00
LOCK ( cs_vRecv ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
nLastRecv = nTimeMicros / 1000000 ;
2020-11-20 06:05:42 +01:00
nRecvBytes + = msg_bytes . size ( ) ;
while ( msg_bytes . size ( ) > 0 ) {
2012-11-16 01:41:12 +01:00
// absorb network data
2020-11-20 06:05:42 +01:00
int handled = m_deserializer - > Read ( msg_bytes ) ;
2022-09-19 21:13:02 +02:00
if ( handled < 0 ) {
// Serious header problem, disconnect from the peer.
return false ;
}
2015-03-05 13:01:22 +01:00
2019-06-13 10:39:44 +02:00
if ( m_deserializer - > Complete ( ) ) {
// decompose a transport agnostic CNetMessage from the deserializer
2022-09-19 21:13:02 +02:00
uint32_t out_err_raw_size { 0 } ;
2022-10-15 22:11:49 +02:00
std : : optional < CNetMessage > result { m_deserializer - > GetMessage ( nTimeMicros , out_err_raw_size ) } ;
2022-09-19 21:13:02 +02:00
if ( ! result ) {
// Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
mapRecvBytesPerMsgCmd . find ( NET_MESSAGE_COMMAND_OTHER ) - > second + = out_err_raw_size ;
continue ;
}
2019-06-13 10:39:44 +02:00
2017-06-29 03:51:10 +02:00
//store received bytes per message command
//to prevent a memory DOS, only allow valid commands
2022-09-19 21:13:02 +02:00
mapMsgCmdSize : : iterator i = mapRecvBytesPerMsgCmd . find ( result - > m_command ) ;
2017-06-29 03:51:10 +02:00
if ( i = = mapRecvBytesPerMsgCmd . end ( ) )
i = mapRecvBytesPerMsgCmd . find ( NET_MESSAGE_COMMAND_OTHER ) ;
assert ( i ! = mapRecvBytesPerMsgCmd . end ( ) ) ;
2022-09-19 21:13:02 +02:00
i - > second + = result - > m_raw_message_size ;
statsClient . count ( " bandwidth.message. " + std : : string ( result - > m_command ) + " .bytesReceived " , result - > m_raw_message_size , 1.0f ) ;
2019-06-13 10:39:44 +02:00
// push the message to the process queue,
2022-09-19 21:13:02 +02:00
vRecvMsg . push_back ( std : : move ( * result ) ) ;
2017-06-29 03:51:10 +02:00
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
2017-07-21 11:35:19 +02:00
complete = true ;
2015-04-05 11:35:37 +02:00
}
2012-11-16 01:41:12 +01:00
}
return true ;
}
2017-08-17 20:37:22 +02:00
void CNode : : SetSendVersion ( int nVersionIn )
{
// Send version may only be changed in the version message, and
// only one version message is allowed per session. We can therefore
// treat this value as const and even atomic as long as it's only used
// once a version message has been successfully processed. Any attempt to
// set this twice is an error.
if ( nSendVersion ! = 0 ) {
error ( " Send version already set for node: %i. Refusing to change from %i to %i " , id , nSendVersion , nVersionIn ) ;
} else {
nSendVersion = nVersionIn ;
}
}
int CNode : : GetSendVersion ( ) const
{
// The send version should always be explicitly set to
// INIT_PROTO_VERSION rather than using this value until SetSendVersion
// has been called.
if ( nSendVersion = = 0 ) {
error ( " Requesting unset send version for node: %i. Using %i " , id , INIT_PROTO_VERSION ) ;
return INIT_PROTO_VERSION ;
}
return nSendVersion ;
}
2020-11-23 10:25:56 +01:00
int V1TransportDeserializer : : readHeader ( Span < const uint8_t > msg_bytes )
2012-11-16 01:41:12 +01:00
{
// copy data to temporary parsing buffer
2020-05-12 10:45:06 +02:00
unsigned int nRemaining = CMessageHeader : : HEADER_SIZE - nHdrPos ;
2020-11-20 06:05:42 +01:00
unsigned int nCopy = std : : min < unsigned int > ( nRemaining , msg_bytes . size ( ) ) ;
2012-11-16 01:41:12 +01:00
2020-11-20 06:05:42 +01:00
memcpy ( & hdrbuf [ nHdrPos ] , msg_bytes . data ( ) , nCopy ) ;
2012-11-16 01:41:12 +01:00
nHdrPos + = nCopy ;
// if header incomplete, exit
2020-05-12 10:45:06 +02:00
if ( nHdrPos < CMessageHeader : : HEADER_SIZE )
2012-11-16 01:41:12 +01:00
return nCopy ;
// deserialize to CMessageHeader
try {
hdrbuf > > hdr ;
}
2014-12-07 13:29:06 +01:00
catch ( const std : : exception & ) {
2022-09-19 21:13:02 +02:00
LogPrint ( BCLog : : NET , " HEADER ERROR - UNABLE TO DESERIALIZE, peer=%d \n " , m_node_id ) ;
return - 1 ;
}
// Check start string, network magic
if ( memcmp ( hdr . pchMessageStart , m_chain_params . MessageStart ( ) , CMessageHeader : : MESSAGE_START_SIZE ) ! = 0 ) {
LogPrint ( BCLog : : NET , " HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d \n " , hdr . GetCommand ( ) , hdr . nMessageSize , HexStr ( hdr . pchMessageStart ) , m_node_id ) ;
2012-11-16 01:41:12 +01:00
return - 1 ;
}
2019-06-13 10:39:44 +02:00
// reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
if ( hdr . nMessageSize > MAX_SIZE | | hdr . nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH ) {
2022-09-19 21:13:02 +02:00
LogPrint ( BCLog : : NET , " HEADER ERROR - SIZE (%s, %u bytes), peer=%d \n " , hdr . GetCommand ( ) , hdr . nMessageSize , m_node_id ) ;
2017-04-26 08:49:22 +02:00
return - 1 ;
2019-06-13 10:39:44 +02:00
}
2012-11-16 01:41:12 +01:00
// switch state to reading message data
in_data = true ;
return nCopy ;
}
2020-11-23 10:25:56 +01:00
int V1TransportDeserializer : : readData ( Span < const uint8_t > msg_bytes )
2012-11-16 01:41:12 +01:00
{
unsigned int nRemaining = hdr . nMessageSize - nDataPos ;
2020-11-20 06:05:42 +01:00
unsigned int nCopy = std : : min < unsigned int > ( nRemaining , msg_bytes . size ( ) ) ;
2012-11-16 01:41:12 +01:00
2014-06-21 17:00:38 +02:00
if ( vRecv . size ( ) < nDataPos + nCopy ) {
// Allocate up to 256 KiB ahead, but never more than the total message size.
vRecv . resize ( std : : min ( hdr . nMessageSize , nDataPos + nCopy + 256 * 1024 ) ) ;
}
2020-11-23 10:25:56 +01:00
hasher . Write ( msg_bytes . first ( nCopy ) ) ;
2020-11-20 06:05:42 +01:00
memcpy ( & vRecv [ nDataPos ] , msg_bytes . data ( ) , nCopy ) ;
2012-11-16 01:41:12 +01:00
nDataPos + = nCopy ;
return nCopy ;
}
2019-06-13 10:39:44 +02:00
const uint256 & V1TransportDeserializer : : GetMessageHash ( ) const
2016-11-07 23:12:26 +01:00
{
2019-06-13 10:39:44 +02:00
assert ( Complete ( ) ) ;
2016-11-07 23:12:26 +01:00
if ( data_hash . IsNull ( ) )
2021-05-19 17:39:18 +02:00
hasher . Finalize ( data_hash ) ;
2016-11-07 23:12:26 +01:00
return data_hash ;
}
2022-10-15 22:11:49 +02:00
std : : optional < CNetMessage > V1TransportDeserializer : : GetMessage ( int64_t time , uint32_t & out_err_raw_size )
2022-09-19 21:13:02 +02:00
{
2019-06-13 10:39:44 +02:00
// decompose a single CNetMessage from the TransportDeserializer
2022-10-15 22:11:49 +02:00
std : : optional < CNetMessage > msg ( std : : move ( vRecv ) ) ;
2019-06-13 10:39:44 +02:00
2022-09-19 21:13:02 +02:00
// store command string, time, and sizes
msg - > m_command = hdr . GetCommand ( ) ;
msg - > m_time = time ;
msg - > m_message_size = hdr . nMessageSize ;
msg - > m_raw_message_size = hdr . nMessageSize + CMessageHeader : : HEADER_SIZE ;
2019-06-13 10:39:44 +02:00
2022-09-19 21:13:02 +02:00
uint256 hash = GetMessageHash ( ) ;
2019-06-13 10:39:44 +02:00
2019-12-04 13:10:58 +01:00
// We just received a message off the wire, harvest entropy from the time (and the message checksum)
RandAddEvent ( ReadLE32 ( hash . begin ( ) ) ) ;
2022-09-19 21:13:02 +02:00
// Check checksum and header command string
if ( memcmp ( hash . begin ( ) , hdr . pchChecksum , CMessageHeader : : CHECKSUM_SIZE ) ! = 0 ) {
LogPrint ( BCLog : : NET , " CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d \n " ,
SanitizeString ( msg - > m_command ) , msg - > m_message_size ,
2021-11-01 21:32:53 +01:00
HexStr ( Span { hash } . first ( CMessageHeader : : CHECKSUM_SIZE ) ) ,
HexStr ( hdr . pchChecksum ) ,
2022-09-19 21:13:02 +02:00
m_node_id ) ;
out_err_raw_size = msg - > m_raw_message_size ;
2022-10-15 22:11:49 +02:00
msg . reset ( ) ;
2022-09-19 21:13:02 +02:00
} else if ( ! hdr . IsCommandValid ( ) ) {
LogPrint ( BCLog : : NET , " HEADER ERROR - COMMAND (%s, %u bytes), peer=%d \n " ,
hdr . GetCommand ( ) , msg - > m_message_size , m_node_id ) ;
out_err_raw_size = msg - > m_raw_message_size ;
2022-10-15 22:11:49 +02:00
msg . reset ( ) ;
2022-09-19 21:13:02 +02:00
}
// Always reset the network deserializer (prepare for the next message)
2019-06-13 10:39:44 +02:00
Reset ( ) ;
return msg ;
}
2019-08-07 15:56:24 +02:00
void V1TransportSerializer : : prepareForTransport ( CSerializedNetMsg & msg , std : : vector < unsigned char > & header ) {
// create dbl-sha256 checksum
2023-09-08 15:34:57 +02:00
uint256 hash = Hash ( msg . data ) ;
2019-08-07 15:56:24 +02:00
// create header
CMessageHeader hdr ( Params ( ) . MessageStart ( ) , msg . command . c_str ( ) , msg . data . size ( ) ) ;
memcpy ( hdr . pchChecksum , hash . begin ( ) , CMessageHeader : : CHECKSUM_SIZE ) ;
// serialize header
header . reserve ( CMessageHeader : : HEADER_SIZE ) ;
CVectorWriter { SER_NETWORK , INIT_PROTO_VERSION , header , 0 , hdr } ;
}
2020-04-07 14:27:06 +02:00
size_t CConnman : : SocketSendData ( CNode * pnode ) EXCLUSIVE_LOCKS_REQUIRED ( pnode - > cs_vSend )
2012-11-16 00:04:52 +01:00
{
2016-11-25 20:01:56 +01:00
auto it = pnode - > vSendMsg . begin ( ) ;
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
2017-07-21 11:35:19 +02:00
size_t nSentSize = 0 ;
2013-03-24 16:52:24 +01:00
while ( it ! = pnode - > vSendMsg . end ( ) ) {
2016-11-25 20:01:56 +01:00
const auto & data = * it ;
2013-03-24 16:52:24 +01:00
assert ( data . size ( ) > pnode - > nSendOffset ) ;
2017-02-10 12:38:45 +01:00
int nBytes = 0 ;
{
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket = = INVALID_SOCKET )
break ;
nBytes = send ( pnode - > hSocket , reinterpret_cast < const char * > ( data . data ( ) ) + pnode - > nSendOffset , data . size ( ) - pnode - > nSendOffset , MSG_NOSIGNAL | MSG_DONTWAIT ) ;
}
2013-03-24 16:52:24 +01:00
if ( nBytes > 0 ) {
2017-08-24 01:38:29 +02:00
pnode - > nLastSend = GetSystemTimeInSeconds ( ) ;
2013-04-07 19:31:13 +02:00
pnode - > nSendBytes + = nBytes ;
2013-03-24 16:52:24 +01:00
pnode - > nSendOffset + = nBytes ;
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
2017-07-21 11:35:19 +02:00
nSentSize + = nBytes ;
2013-03-24 16:52:24 +01:00
if ( pnode - > nSendOffset = = data . size ( ) ) {
pnode - > nSendOffset = 0 ;
pnode - > nSendSize - = data . size ( ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
pnode - > fPauseSend = pnode - > nSendSize > nSendBufferMaxSize ;
2013-03-24 16:52:24 +01:00
it + + ;
} else {
// could not send full message; stop sending more
2020-04-07 17:52:20 +02:00
pnode - > fCanSendData = false ;
2013-03-24 16:52:24 +01:00
break ;
}
} else {
if ( nBytes < 0 ) {
// error
int nErr = WSAGetLastError ( ) ;
if ( nErr ! = WSAEWOULDBLOCK & & nErr ! = WSAEMSGSIZE & & nErr ! = WSAEINTR & & nErr ! = WSAEINPROGRESS )
{
2020-03-21 11:33:37 +01:00
LogPrintf ( " socket send error %s (peer=%d) \n " , NetworkErrorString ( nErr ) , pnode - > GetId ( ) ) ;
2017-01-19 20:02:57 +01:00
pnode - > fDisconnect = true ;
2013-03-24 16:52:24 +01:00
}
}
// couldn't send anything at all
2020-04-07 17:52:20 +02:00
pnode - > fCanSendData = false ;
2013-03-24 16:52:24 +01:00
break ;
2012-11-16 00:04:52 +01:00
}
}
2013-03-24 16:52:24 +01:00
if ( it = = pnode - > vSendMsg . end ( ) ) {
assert ( pnode - > nSendOffset = = 0 ) ;
assert ( pnode - > nSendSize = = 0 ) ;
}
pnode - > vSendMsg . erase ( pnode - > vSendMsg . begin ( ) , it ) ;
2020-04-07 17:53:26 +02:00
pnode - > nSendMsgSize = pnode - > vSendMsg . size ( ) ;
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
2017-07-21 11:35:19 +02:00
return nSentSize ;
2012-11-16 00:04:52 +01:00
}
2010-08-29 18:58:15 +02:00
2017-03-13 07:29:16 +01:00
struct NodeEvictionCandidate
{
2016-06-08 18:05:01 +02:00
NodeId id ;
2017-03-13 07:29:16 +01:00
int64_t nTimeConnected ;
int64_t nMinPingUsecTime ;
2017-07-12 13:13:38 +02:00
int64_t nLastBlockTime ;
int64_t nLastTXTime ;
2016-11-07 13:13:29 +01:00
bool fRelevantServices ;
2017-07-12 13:13:38 +02:00
bool fRelayTxes ;
bool fBloomFilter ;
2016-06-08 18:05:01 +02:00
uint64_t nKeyedNetGroup ;
2019-01-30 00:40:45 +01:00
bool prefer_evict ;
2015-08-21 02:29:04 +02:00
} ;
2017-03-13 07:29:16 +01:00
static bool ReverseCompareNodeMinPingTime ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b )
2015-08-13 11:58:58 +02:00
{
2017-03-13 07:29:16 +01:00
return a . nMinPingUsecTime > b . nMinPingUsecTime ;
2015-08-13 11:58:58 +02:00
}
2017-03-13 07:29:16 +01:00
static bool ReverseCompareNodeTimeConnected ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b )
2015-08-13 11:58:58 +02:00
{
2017-03-13 07:29:16 +01:00
return a . nTimeConnected > b . nTimeConnected ;
2015-08-13 11:58:58 +02:00
}
2016-06-08 18:05:01 +02:00
static bool CompareNetGroupKeyed ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b ) {
return a . nKeyedNetGroup < b . nKeyedNetGroup ;
2018-02-08 06:44:07 +01:00
}
2015-08-13 11:58:58 +02:00
2017-07-12 13:13:38 +02:00
static bool CompareNodeBlockTime ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b )
{
// There is a fall-through here because it is common for a node to have many peers which have not yet relayed a block.
if ( a . nLastBlockTime ! = b . nLastBlockTime ) return a . nLastBlockTime < b . nLastBlockTime ;
2016-11-07 13:13:29 +01:00
if ( a . fRelevantServices ! = b . fRelevantServices ) return b . fRelevantServices ;
2017-07-12 13:13:38 +02:00
return a . nTimeConnected > b . nTimeConnected ;
}
static bool CompareNodeTXTime ( const NodeEvictionCandidate & a , const NodeEvictionCandidate & b )
{
// There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn.
if ( a . nLastTXTime ! = b . nLastTXTime ) return a . nLastTXTime < b . nLastTXTime ;
if ( a . fRelayTxes ! = b . fRelayTxes ) return b . fRelayTxes ;
if ( a . fBloomFilter ! = b . fBloomFilter ) return a . fBloomFilter ;
return a . nTimeConnected > b . nTimeConnected ;
}
2017-11-08 08:33:35 +01:00
//! Sort an array by the specified comparator, then erase the last K elements.
template < typename T , typename Comparator >
static void EraseLastKElements ( std : : vector < T > & elements , Comparator comparator , size_t k )
{
std : : sort ( elements . begin ( ) , elements . end ( ) , comparator ) ;
size_t eraseSize = std : : min ( k , elements . size ( ) ) ;
elements . erase ( elements . end ( ) - eraseSize , elements . end ( ) ) ;
}
2017-07-12 13:13:38 +02:00
/** Try to find a connection to evict when the node is full.
* Extreme care must be taken to avoid opening the node to attacker
* triggered network partitioning .
* The strategy used here is to protect a small number of peers
* for each of several distinct characteristics which are difficult
* to forge . In order to partition a node the attacker must be
* simultaneously better at all of them than honest peers .
*/
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
2017-07-21 11:35:19 +02:00
bool CConnman : : AttemptToEvictConnection ( )
{
2017-03-13 07:29:16 +01:00
std : : vector < NodeEvictionCandidate > vEvictionCandidates ;
2015-08-13 11:58:58 +02:00
{
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( const CNode * node : vNodes ) {
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
if ( node - > HasPermission ( PF_NOBAN ) )
2015-08-13 11:58:58 +02:00
continue ;
2017-09-19 16:52:27 +02:00
if ( ! node - > fInbound )
2015-08-13 11:58:58 +02:00
continue ;
2017-09-19 16:52:27 +02:00
if ( node - > fDisconnect )
2015-08-13 11:58:58 +02:00
continue ;
2019-03-22 11:52:37 +01:00
if ( fMasternodeMode ) {
// This handles eviction protected nodes. Nodes are always protected for a short time after the connection
// was accepted. This short time is meant for the VERSION/VERACK exchange and the possible MNAUTH that might
2020-02-01 02:59:01 +01:00
// follow when the incoming connection is from another masternode. When a message other than MNAUTH
2019-03-22 11:52:37 +01:00
// is received after VERSION/VERACK, the protection is lifted immediately.
bool isProtected = GetSystemTimeInSeconds ( ) - node - > nTimeConnected < INBOUND_EVICTION_PROTECTION_TIME ;
if ( node - > nTimeFirstMessageReceived ! = 0 & & ! node - > fFirstMessageIsMNAUTH ) {
isProtected = false ;
}
// if MNAUTH was valid, the node is always protected (and at the same time not accounted when
// checking incoming connection limits)
2021-07-26 17:52:52 +02:00
if ( ! node - > GetVerifiedProRegTxHash ( ) . IsNull ( ) ) {
2019-03-22 11:52:37 +01:00
isProtected = true ;
}
if ( isProtected ) {
continue ;
}
}
2022-06-19 08:02:28 +02:00
bool peer_relay_txes = false ;
bool peer_filter_not_null = false ;
2023-04-19 16:57:27 +02:00
if ( node - > IsAddrRelayPeer ( ) ) {
2022-06-19 08:02:28 +02:00
LOCK ( node - > m_tx_relay - > cs_filter ) ;
peer_relay_txes = node - > m_tx_relay - > fRelayTxes ;
peer_filter_not_null = node - > m_tx_relay - > pfilter ! = nullptr ;
}
2017-05-07 09:59:42 +02:00
NodeEvictionCandidate candidate = { node - > GetId ( ) , node - > nTimeConnected , node - > nMinPingUsecTime ,
2016-11-07 13:13:29 +01:00
node - > nLastBlockTime , node - > nLastTXTime ,
2017-10-14 00:25:16 +02:00
HasAllDesirableServiceFlags ( node - > nServices ) ,
2022-06-19 08:02:28 +02:00
peer_relay_txes , peer_filter_not_null , node - > nKeyedNetGroup ,
2019-01-30 00:40:45 +01:00
node - > m_prefer_evict } ;
2017-09-19 16:52:27 +02:00
vEvictionCandidates . push_back ( candidate ) ;
2015-08-13 11:58:58 +02:00
}
}
// Protect connections with certain characteristics
2015-08-21 01:47:49 +02:00
// Deterministically select 4 peers to protect by netgroup.
2016-06-08 18:05:01 +02:00
// An attacker cannot predict which netgroups will be protected
2017-11-08 08:33:35 +01:00
EraseLastKElements ( vEvictionCandidates , CompareNetGroupKeyed , 4 ) ;
2016-05-04 15:51:38 +02:00
// Protect the 8 nodes with the lowest minimum ping time.
2015-08-21 01:47:49 +02:00
// An attacker cannot manipulate this metric without physically moving nodes closer to the target.
2017-11-08 08:33:35 +01:00
EraseLastKElements ( vEvictionCandidates , ReverseCompareNodeMinPingTime , 8 ) ;
2020-09-02 14:10:21 +02:00
// Protect 4 nodes that most recently sent us novel transactions accepted into our mempool.
2017-07-12 13:13:38 +02:00
// An attacker cannot manipulate this metric without performing useful work.
2017-11-08 08:33:35 +01:00
EraseLastKElements ( vEvictionCandidates , CompareNodeTXTime , 4 ) ;
2020-09-02 14:10:21 +02:00
// Protect 4 nodes that most recently sent us novel blocks.
2017-07-12 13:13:38 +02:00
// An attacker cannot manipulate this metric without performing useful work.
2017-11-08 08:33:35 +01:00
EraseLastKElements ( vEvictionCandidates , CompareNodeBlockTime , 4 ) ;
2015-08-26 01:31:13 +02:00
// Protect the half of the remaining nodes which have been connected the longest.
2016-05-04 15:51:38 +02:00
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
2017-11-08 08:33:35 +01:00
EraseLastKElements ( vEvictionCandidates , ReverseCompareNodeTimeConnected , vEvictionCandidates . size ( ) / 2 ) ;
2015-08-13 11:58:58 +02:00
2015-08-23 00:15:39 +02:00
if ( vEvictionCandidates . empty ( ) ) return false ;
2015-08-13 11:58:58 +02:00
2019-01-30 00:40:45 +01:00
// If any remaining peers are preferred for eviction consider only them.
// This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks)
// then we probably don't want to evict it no matter what.
if ( std : : any_of ( vEvictionCandidates . begin ( ) , vEvictionCandidates . end ( ) , [ ] ( NodeEvictionCandidate const & n ) { return n . prefer_evict ; } ) ) {
vEvictionCandidates . erase ( std : : remove_if ( vEvictionCandidates . begin ( ) , vEvictionCandidates . end ( ) ,
[ ] ( NodeEvictionCandidate const & n ) { return ! n . prefer_evict ; } ) , vEvictionCandidates . end ( ) ) ;
}
2015-11-23 04:48:54 +01:00
// Identify the network group with the most connections and youngest member.
// (vEvictionCandidates is already sorted by reverse connect time)
2016-06-08 18:05:01 +02:00
uint64_t naMostConnections ;
2015-08-13 11:58:58 +02:00
unsigned int nMostConnections = 0 ;
2015-11-23 04:48:54 +01:00
int64_t nMostConnectionsTime = 0 ;
2016-08-03 11:50:10 +02:00
std : : map < uint64_t , std : : vector < NodeEvictionCandidate > > mapNetGroupNodes ;
2019-07-05 09:06:28 +02:00
for ( const NodeEvictionCandidate & node : vEvictionCandidates ) {
2017-11-08 08:33:35 +01:00
std : : vector < NodeEvictionCandidate > & group = mapNetGroupNodes [ node . nKeyedNetGroup ] ;
group . push_back ( node ) ;
int64_t grouptime = group [ 0 ] . nTimeConnected ;
2015-08-13 11:58:58 +02:00
2017-11-08 08:33:35 +01:00
if ( group . size ( ) > nMostConnections | | ( group . size ( ) = = nMostConnections & & grouptime > nMostConnectionsTime ) ) {
nMostConnections = group . size ( ) ;
2015-11-23 04:48:54 +01:00
nMostConnectionsTime = grouptime ;
2016-06-08 18:05:01 +02:00
naMostConnections = node . nKeyedNetGroup ;
2015-08-13 11:58:58 +02:00
}
}
2015-08-26 02:06:15 +02:00
// Reduce to the network group with the most connections
2016-08-03 11:50:10 +02:00
vEvictionCandidates = std : : move ( mapNetGroupNodes [ naMostConnections ] ) ;
2015-08-13 11:58:58 +02:00
2015-11-23 04:48:54 +01:00
// Disconnect from the network group with the most connections
2016-06-08 18:05:01 +02:00
NodeId evicted = vEvictionCandidates . front ( ) . id ;
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( CNode * pnode : vNodes ) {
if ( pnode - > GetId ( ) = = evicted ) {
pnode - > fDisconnect = true ;
2016-06-08 18:05:01 +02:00
return true ;
2017-03-13 07:29:16 +01:00
}
}
return false ;
2015-08-13 11:58:58 +02:00
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : AcceptConnection ( const ListenSocket & hListenSocket ) {
2015-08-13 11:00:10 +02:00
struct sockaddr_storage sockaddr ;
socklen_t len = sizeof ( sockaddr ) ;
SOCKET hSocket = accept ( hListenSocket . socket , ( struct sockaddr * ) & sockaddr , & len ) ;
CAddress addr ;
2020-11-18 17:13:27 +01:00
if ( hSocket = = INVALID_SOCKET ) {
const int nErr = WSAGetLastError ( ) ;
if ( nErr ! = WSAEWOULDBLOCK ) {
LogPrintf ( " socket error accept failed: %s \n " , NetworkErrorString ( nErr ) ) ;
2017-06-05 15:39:11 +02:00
}
2020-11-18 17:13:27 +01:00
return ;
}
if ( ! addr . SetSockAddr ( ( const struct sockaddr * ) & sockaddr ) ) {
LogPrintf ( " Warning: Unknown socket family \n " ) ;
2017-06-05 15:39:11 +02:00
}
2015-08-13 11:00:10 +02:00
2020-11-18 17:13:27 +01:00
const CAddress addr_bind = GetBindAddress ( hSocket ) ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
NetPermissionFlags permissionFlags = NetPermissionFlags : : PF_NONE ;
hListenSocket . AddSocketPermissionFlags ( permissionFlags ) ;
2020-11-18 17:13:27 +01:00
CreateNodeFromAcceptedSocket ( hSocket , permissionFlags , addr_bind , addr ) ;
}
void CConnman : : CreateNodeFromAcceptedSocket ( SOCKET hSocket ,
NetPermissionFlags permissionFlags ,
const CAddress & addr_bind ,
const CAddress & addr )
{
int nInbound = 0 ;
int nVerifiedInboundMasternodes = 0 ;
int nMaxInbound = nMaxConnections - m_max_outbound ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
AddWhitelistPermissionFlags ( permissionFlags , addr ) ;
bool legacyWhitelisted = false ;
if ( NetPermissions : : HasFlag ( permissionFlags , NetPermissionFlags : : PF_ISIMPLICIT ) ) {
NetPermissions : : ClearFlag ( permissionFlags , PF_ISIMPLICIT ) ;
2019-08-19 11:18:47 +02:00
if ( gArgs . GetBoolArg ( " -whitelistforcerelay " , DEFAULT_WHITELISTFORCERELAY ) ) NetPermissions : : AddFlag ( permissionFlags , PF_FORCERELAY ) ;
if ( gArgs . GetBoolArg ( " -whitelistrelay " , DEFAULT_WHITELISTRELAY ) ) NetPermissions : : AddFlag ( permissionFlags , PF_RELAY ) ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
NetPermissions : : AddFlag ( permissionFlags , PF_MEMPOOL ) ;
NetPermissions : : AddFlag ( permissionFlags , PF_NOBAN ) ;
legacyWhitelisted = true ;
}
2015-08-13 11:00:10 +02:00
{
LOCK ( cs_vNodes ) ;
2017-09-21 01:32:56 +02:00
for ( const CNode * pnode : vNodes ) {
2019-03-22 11:52:37 +01:00
if ( pnode - > fInbound ) {
2015-08-13 11:00:10 +02:00
nInbound + + ;
2021-07-26 17:52:52 +02:00
if ( ! pnode - > GetVerifiedProRegTxHash ( ) . IsNull ( ) ) {
2019-03-22 11:52:37 +01:00
nVerifiedInboundMasternodes + + ;
}
}
}
2015-08-13 11:00:10 +02:00
}
2019-09-22 22:48:15 +02:00
std : : string strDropped ;
if ( fLogIPs ) {
strDropped = strprintf ( " connection from %s dropped " , addr . ToString ( ) ) ;
} else {
strDropped = " connection dropped " ;
}
2017-09-11 15:38:14 +02:00
if ( ! fNetworkActive ) {
2019-09-22 22:48:15 +02:00
LogPrintf ( " %s: not accepting new connections \n " , strDropped ) ;
2017-09-11 15:38:14 +02:00
CloseSocket ( hSocket ) ;
return ;
}
2015-08-13 11:16:46 +02:00
if ( ! IsSelectableSocket ( hSocket ) )
2015-08-13 11:00:10 +02:00
{
2019-09-22 22:48:15 +02:00
LogPrintf ( " %s: non-selectable socket \n " , strDropped ) ;
2015-08-13 11:00:10 +02:00
CloseSocket ( hSocket ) ;
2015-08-13 11:16:46 +02:00
return ;
2015-08-13 11:00:10 +02:00
}
2015-08-13 11:16:46 +02:00
2015-10-22 01:52:29 +02:00
// According to the internet TCP_NODELAY is not carried into accepted sockets
// on all platforms. Set it again here just to be sure.
2017-05-18 02:26:54 +02:00
SetSocketNoDelay ( hSocket ) ;
2015-10-22 01:52:29 +02:00
2022-06-10 11:16:05 +02:00
// Don't accept connections from banned peers.
bool banned = m_banman - > IsBanned ( addr ) ;
if ( ! NetPermissions : : HasFlag ( permissionFlags , NetPermissionFlags : : PF_NOBAN ) & & banned )
2015-08-13 11:00:10 +02:00
{
2017-12-11 16:44:02 +01:00
LogPrint ( BCLog : : NET , " %s (banned) \n " , strDropped ) ;
2015-08-13 11:00:10 +02:00
CloseSocket ( hSocket ) ;
2015-08-13 11:16:46 +02:00
return ;
2015-08-13 11:00:10 +02:00
}
2015-08-13 11:16:46 +02:00
2022-06-10 11:16:05 +02:00
// Only accept connections from discouraged peers if our inbound slots aren't (almost) full.
bool discouraged = m_banman - > IsDiscouraged ( addr ) ;
if ( ! NetPermissions : : HasFlag ( permissionFlags , NetPermissionFlags : : PF_NOBAN ) & & nInbound + 1 > = nMaxInbound & & discouraged )
{
LogPrint ( BCLog : : NET , " connection from %s dropped (discouraged) \n " , addr . ToString ( ) ) ;
CloseSocket ( hSocket ) ;
return ;
}
2019-03-22 11:52:37 +01:00
// Evict connections until we are below nMaxInbound. In case eviction protection resulted in nodes to not be evicted
// before, they might get evicted in batches now (after the protection timeout).
// We don't evict verified MN connections and also don't take them into account when checking limits. We can do this
// because we know that such connections are naturally limited by the total number of MNs, so this is not usable
// for attacks.
while ( nInbound - nVerifiedInboundMasternodes > = nMaxInbound )
2015-08-13 11:00:10 +02:00
{
2017-07-12 13:13:38 +02:00
if ( ! AttemptToEvictConnection ( ) ) {
2015-08-13 11:58:58 +02:00
// No connection to evict, disconnect the new connection
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " failed to find an eviction candidate - connection dropped (full) \n " ) ;
2015-08-13 11:58:58 +02:00
CloseSocket ( hSocket ) ;
return ;
}
2019-03-22 11:52:37 +01:00
nInbound - - ;
2015-08-13 11:00:10 +02:00
}
2022-07-06 23:25:02 +02:00
// don't accept incoming connections until blockchain is synced
refactor: begin to de-globalize masternodeSync (#5103)
<!--
*** Please remove the following help text before submitting: ***
Provide a general summary of your changes in the Title above
Pull requests without a rationale and clear improvement may be closed
immediately.
Please provide clear motivation for your patch and explain how it
improves
Dash Core user experience or Dash Core developer experience
significantly:
* Any test improvements or new tests that improve coverage are always
welcome.
* All other changes should have accompanying unit tests (see
`src/test/`) or
functional tests (see `test/`). Contributors should note which tests
cover
modified code. If no tests exist for a region of modified code, new
tests
should accompany the change.
* Bug fixes are most welcome when they come with steps to reproduce or
an
explanation of the potential issue as well as reasoning for the way the
bug
was fixed.
* Features are welcome, but might be rejected due to design or scope
issues.
If a feature is based on a lot of dependencies, contributors should
first
consider building the system outside of Dash Core, if possible.
-->
## Issue being fixed or feature implemented
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
minimizing global uses
## What was done?
<!--- Describe your changes in detail -->
Started the deglobalization, a future PR should be done to continue this
deglobalization
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
## Breaking Changes
<!--- Please describe any breaking changes your code introduces -->
none
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation
**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2023-01-04 21:37:20 +01:00
if ( fMasternodeMode & & ! : : masternodeSync - > IsBlockchainSynced ( ) ) {
2022-07-06 23:25:02 +02:00
LogPrint ( BCLog : : NET , " AcceptConnection -- blockchain is not synced yet, skipping inbound connection attempt \n " ) ;
2017-02-16 16:40:40 +01:00
CloseSocket ( hSocket ) ;
return ;
}
2016-11-03 10:45:11 +01:00
NodeId id = GetNewNodeId ( ) ;
uint64_t nonce = GetDeterministicRandomizer ( RANDOMIZER_ID_LOCALHOSTNONCE ) . Write ( id ) . Finalize ( ) ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
ServiceFlags nodeServices = nLocalServices ;
if ( NetPermissions : : HasFlag ( permissionFlags , PF_BLOOMFILTER ) ) {
nodeServices = static_cast < ServiceFlags > ( nodeServices | NODE_BLOOM ) ;
}
2023-04-16 06:56:24 +02:00
const bool inbound_onion = std : : find ( m_onion_binds . begin ( ) , m_onion_binds . end ( ) , addr_bind ) ! = m_onion_binds . end ( ) ;
2023-04-28 08:36:19 +02:00
CNode * pnode = new CNode ( id , nodeServices , hSocket , addr , CalculateKeyedNetGroup ( addr ) , nonce , addr_bind , " " , true , inbound_onion ) ;
2018-01-17 17:45:38 +01:00
pnode - > AddRef ( ) ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
pnode - > m_permissionFlags = permissionFlags ;
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
pnode - > m_legacyWhitelisted = legacyWhitelisted ;
2022-06-10 11:16:05 +02:00
pnode - > m_prefer_evict = discouraged ;
2017-09-08 01:00:49 +02:00
m_msgproc - > InitializeNode ( pnode ) ;
2015-08-13 11:00:10 +02:00
2019-09-22 22:48:15 +02:00
if ( fLogIPs ) {
2021-06-23 10:10:42 +02:00
LogPrint ( BCLog : : NET_NETCONN , " connection from %s accepted, sock=%d, peer=%d \n " , addr . ToString ( ) , hSocket , pnode - > GetId ( ) ) ;
2019-09-22 22:48:15 +02:00
} else {
2021-06-23 10:10:42 +02:00
LogPrint ( BCLog : : NET_NETCONN , " connection accepted, sock=%d, peer=%d \n " , hSocket , pnode - > GetId ( ) ) ;
2019-09-22 22:48:15 +02:00
}
2015-08-13 11:16:46 +02:00
{
LOCK ( cs_vNodes ) ;
vNodes . push_back ( pnode ) ;
2021-06-23 10:10:42 +02:00
mapSocketToNode . emplace ( hSocket , pnode ) ;
2020-04-07 17:58:38 +02:00
RegisterEvents ( pnode ) ;
2020-04-08 23:17:19 +02:00
WakeSelect ( ) ;
2015-08-13 11:00:10 +02:00
}
2019-12-04 13:10:58 +01:00
// We received a new connection, harvest entropy from the time (and our peer count)
RandAddEvent ( ( uint32_t ) id ) ;
2015-08-13 11:00:10 +02:00
}
2018-09-24 22:36:58 +02:00
void CConnman : : DisconnectNodes ( )
2018-09-24 22:30:53 +02:00
{
{
2020-04-21 18:23:46 +02:00
LOCK ( cs_vNodes ) ;
2018-07-22 16:31:11 +02:00
if ( ! fNetworkActive ) {
// Disconnect any connected nodes
for ( CNode * pnode : vNodes ) {
if ( ! pnode - > fDisconnect ) {
LogPrint ( BCLog : : NET , " Network not active, dropping peer=%d \n " , pnode - > GetId ( ) ) ;
pnode - > fDisconnect = true ;
}
}
}
2018-09-24 22:36:58 +02:00
// Disconnect unused nodes
for ( auto it = vNodes . begin ( ) ; it ! = vNodes . end ( ) ; )
2010-08-29 18:58:15 +02:00
{
2018-09-24 22:36:58 +02:00
CNode * pnode = * it ;
if ( pnode - > fDisconnect )
2010-08-29 18:58:15 +02:00
{
2020-04-22 07:50:40 +02:00
// If we were the ones who initiated the disconnect, we must assume that the other side wants to see
// pending messages. If the other side initiated the disconnect (or disconnected after we've shutdown
// the socket), we can be pretty sure that they are not interested in any pending messages anymore and
// thus can immediately close the socket.
if ( ! pnode - > fOtherSideDisconnected ) {
if ( pnode - > nDisconnectLingerTime = = 0 ) {
// let's not immediately close the socket but instead wait for at least 100ms so that there is a
// chance to flush all/some pending data. Otherwise the other side might not receive REJECT messages
// that were pushed right before setting fDisconnect=true
// Flushing must happen in two places to ensure data can be received by the other side:
// 1. vSendMsg must be empty and all messages sent via send(). This is ensured by SocketHandler()
// being called before DisconnectNodes and also by the linger time
// 2. Internal socket send buffers must be flushed. This is ensured solely by the linger time
pnode - > nDisconnectLingerTime = GetTimeMillis ( ) + 100 ;
}
if ( GetTimeMillis ( ) < pnode - > nDisconnectLingerTime ) {
// everything flushed to the kernel?
if ( ! pnode - > fSocketShutdown & & pnode - > nSendMsgSize = = 0 ) {
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket ! = INVALID_SOCKET ) {
// Give the other side a chance to detect the disconnect as early as possible (recv() will return 0)
: : shutdown ( pnode - > hSocket , SD_SEND ) ;
}
pnode - > fSocketShutdown = true ;
}
+ + it ;
continue ;
}
2020-04-17 12:05:30 +02:00
}
2018-09-24 22:36:58 +02:00
if ( fLogIPs ) {
2021-03-15 03:49:38 +01:00
LogPrintf ( " ThreadSocketHandler -- removing node: peer=%d addr=%s nRefCount=%d fInbound=%d m_masternode_connection=%d m_masternode_iqr_connection=%d \n " ,
pnode - > GetId ( ) , pnode - > addr . ToString ( ) , pnode - > GetRefCount ( ) , pnode - > fInbound , pnode - > m_masternode_connection , pnode - > m_masternode_iqr_connection ) ;
2018-09-24 22:36:58 +02:00
} else {
2021-03-15 03:49:38 +01:00
LogPrintf ( " ThreadSocketHandler -- removing node: peer=%d nRefCount=%d fInbound=%d m_masternode_connection=%d m_masternode_iqr_connection=%d \n " ,
pnode - > GetId ( ) , pnode - > GetRefCount ( ) , pnode - > fInbound , pnode - > m_masternode_connection , pnode - > m_masternode_iqr_connection ) ;
2018-09-24 22:36:58 +02:00
}
2017-02-05 17:45:36 +01:00
2018-09-24 22:36:58 +02:00
// remove from vNodes
it = vNodes . erase ( it ) ;
2010-08-29 18:58:15 +02:00
2018-09-24 22:36:58 +02:00
// release outbound grant (if any)
pnode - > grantOutbound . Release ( ) ;
2012-04-04 16:01:57 +02:00
2018-09-24 22:36:58 +02:00
// close socket and cleanup
2020-04-07 07:00:41 +02:00
pnode - > CloseSocketDisconnect ( this ) ;
2010-08-29 18:58:15 +02:00
2018-09-24 22:36:58 +02:00
// hold in disconnected pool until all refs are released
pnode - > Release ( ) ;
vNodesDisconnected . push_back ( pnode ) ;
} else {
+ + it ;
2010-08-29 18:58:15 +02:00
}
2013-07-25 02:25:25 +02:00
}
2018-09-24 22:36:58 +02:00
}
{
// Delete disconnected nodes
std : : list < CNode * > vNodesDisconnectedCopy = vNodesDisconnected ;
for ( auto it = vNodesDisconnected . begin ( ) ; it ! = vNodesDisconnected . end ( ) ; )
2013-07-25 02:25:25 +02:00
{
2018-09-24 22:36:58 +02:00
CNode * pnode = * it ;
// wait until threads are done using it
bool fDelete = false ;
if ( pnode - > GetRefCount ( ) < = 0 ) {
{
TRY_LOCK ( pnode - > cs_inventory , lockInv ) ;
if ( lockInv ) {
TRY_LOCK ( pnode - > cs_vSend , lockSend ) ;
if ( lockSend ) {
fDelete = true ;
2012-04-06 18:39:12 +02:00
}
}
2010-08-29 18:58:15 +02:00
}
2018-09-24 22:36:58 +02:00
if ( fDelete ) {
it = vNodesDisconnected . erase ( it ) ;
2020-04-21 18:23:46 +02:00
DeleteNode ( pnode ) ;
2020-04-07 06:55:09 +02:00
}
2010-08-29 18:58:15 +02:00
}
2018-09-24 22:36:58 +02:00
if ( ! fDelete ) {
+ + it ;
}
2010-08-29 18:58:15 +02:00
}
2018-09-24 22:36:58 +02:00
}
}
void CConnman : : NotifyNumConnectionsChanged ( )
{
size_t vNodesSize ;
{
LOCK ( cs_vNodes ) ;
vNodesSize = vNodes . size ( ) ;
}
2020-09-11 14:07:34 +02:00
// If we had zero connections before and new connections now or if we just dropped
// to zero connections reset the sync process if its outdated.
if ( ( vNodesSize > 0 & & nPrevNodeCount = = 0 ) | | ( vNodesSize = = 0 & & nPrevNodeCount > 0 ) ) {
refactor: begin to de-globalize masternodeSync (#5103)
<!--
*** Please remove the following help text before submitting: ***
Provide a general summary of your changes in the Title above
Pull requests without a rationale and clear improvement may be closed
immediately.
Please provide clear motivation for your patch and explain how it
improves
Dash Core user experience or Dash Core developer experience
significantly:
* Any test improvements or new tests that improve coverage are always
welcome.
* All other changes should have accompanying unit tests (see
`src/test/`) or
functional tests (see `test/`). Contributors should note which tests
cover
modified code. If no tests exist for a region of modified code, new
tests
should accompany the change.
* Bug fixes are most welcome when they come with steps to reproduce or
an
explanation of the potential issue as well as reasoning for the way the
bug
was fixed.
* Features are welcome, but might be rejected due to design or scope
issues.
If a feature is based on a lot of dependencies, contributors should
first
consider building the system outside of Dash Core, if possible.
-->
## Issue being fixed or feature implemented
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
minimizing global uses
## What was done?
<!--- Describe your changes in detail -->
Started the deglobalization, a future PR should be done to continue this
deglobalization
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
## Breaking Changes
<!--- Please describe any breaking changes your code introduces -->
none
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation
**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2023-01-04 21:37:20 +01:00
: : masternodeSync - > Reset ( ) ;
2020-09-11 14:07:34 +02:00
}
2018-09-24 22:36:58 +02:00
if ( vNodesSize ! = nPrevNodeCount ) {
nPrevNodeCount = vNodesSize ;
if ( clientInterface )
2018-09-04 12:21:16 +02:00
clientInterface - > NotifyNumConnectionsChanged ( vNodesSize ) ;
2020-12-15 17:22:23 +01:00
CalculateNumConnectionsChangedStats ( ) ;
2018-09-24 22:36:58 +02:00
}
}
2020-12-15 17:22:23 +01:00
void CConnman : : CalculateNumConnectionsChangedStats ( )
{
if ( ! gArgs . GetBoolArg ( " -statsenabled " , DEFAULT_STATSD_ENABLE ) ) {
return ;
}
// count various node attributes for statsD
int fullNodes = 0 ;
int spvNodes = 0 ;
int inboundNodes = 0 ;
int outboundNodes = 0 ;
int ipv4Nodes = 0 ;
int ipv6Nodes = 0 ;
int torNodes = 0 ;
mapMsgCmdSize mapRecvBytesMsgStats ;
mapMsgCmdSize mapSentBytesMsgStats ;
for ( const std : : string & msg : getAllNetMessageTypes ( ) ) {
mapRecvBytesMsgStats [ msg ] = 0 ;
mapSentBytesMsgStats [ msg ] = 0 ;
}
mapRecvBytesMsgStats [ NET_MESSAGE_COMMAND_OTHER ] = 0 ;
mapSentBytesMsgStats [ NET_MESSAGE_COMMAND_OTHER ] = 0 ;
2021-06-23 10:10:42 +02:00
auto vNodesCopy = CopyNodeVector ( CConnman : : FullyConnectedOnly ) ;
for ( auto pnode : vNodesCopy ) {
2021-01-06 07:07:32 +01:00
{
LOCK ( pnode - > cs_vRecv ) ;
for ( const mapMsgCmdSize : : value_type & i : pnode - > mapRecvBytesPerMsgCmd )
mapRecvBytesMsgStats [ i . first ] + = i . second ;
}
{
LOCK ( pnode - > cs_vSend ) ;
for ( const mapMsgCmdSize : : value_type & i : pnode - > mapSendBytesPerMsgCmd )
mapSentBytesMsgStats [ i . first ] + = i . second ;
}
2020-12-15 17:22:23 +01:00
if ( pnode - > fClient )
spvNodes + + ;
else
fullNodes + + ;
if ( pnode - > fInbound )
inboundNodes + + ;
else
outboundNodes + + ;
if ( pnode - > addr . IsIPv4 ( ) )
ipv4Nodes + + ;
if ( pnode - > addr . IsIPv6 ( ) )
ipv6Nodes + + ;
if ( pnode - > addr . IsTor ( ) )
torNodes + + ;
if ( pnode - > nPingUsecTime > 0 )
statsClient . timing ( " peers.ping_us " , pnode - > nPingUsecTime , 1.0f ) ;
}
2021-06-23 10:10:42 +02:00
ReleaseNodeVector ( vNodesCopy ) ;
2020-12-15 17:22:23 +01:00
for ( const std : : string & msg : getAllNetMessageTypes ( ) ) {
statsClient . gauge ( " bandwidth.message. " + msg + " .totalBytesReceived " , mapRecvBytesMsgStats [ msg ] , 1.0f ) ;
statsClient . gauge ( " bandwidth.message. " + msg + " .totalBytesSent " , mapSentBytesMsgStats [ msg ] , 1.0f ) ;
}
statsClient . gauge ( " peers.totalConnections " , nPrevNodeCount , 1.0f ) ;
statsClient . gauge ( " peers.spvNodeConnections " , spvNodes , 1.0f ) ;
statsClient . gauge ( " peers.fullNodeConnections " , fullNodes , 1.0f ) ;
statsClient . gauge ( " peers.inboundConnections " , inboundNodes , 1.0f ) ;
statsClient . gauge ( " peers.outboundConnections " , outboundNodes , 1.0f ) ;
statsClient . gauge ( " peers.ipv4Connections " , ipv4Nodes , 1.0f ) ;
statsClient . gauge ( " peers.ipv6Connections " , ipv6Nodes , 1.0f ) ;
statsClient . gauge ( " peers.torConnections " , torNodes , 1.0f ) ;
}
2021-01-07 09:03:35 +01:00
void CConnman : : InactivityCheck ( CNode * pnode ) const
2018-09-24 22:43:00 +02:00
{
int64_t nTime = GetSystemTimeInSeconds ( ) ;
2018-12-04 12:06:35 +01:00
if ( nTime - pnode - > nTimeConnected > m_peer_connect_timeout )
2018-09-24 22:43:00 +02:00
{
if ( pnode - > nLastRecv = = 0 | | pnode - > nLastSend = = 0 )
{
2018-12-04 12:06:35 +01:00
LogPrint ( BCLog : : NET , " socket no message in first %i seconds, %d %d from %d \n " , m_peer_connect_timeout , pnode - > nLastRecv ! = 0 , pnode - > nLastSend ! = 0 , pnode - > GetId ( ) ) ;
2018-09-24 22:43:00 +02:00
pnode - > fDisconnect = true ;
}
else if ( nTime - pnode - > nLastSend > TIMEOUT_INTERVAL )
{
LogPrintf ( " socket sending timeout: %is \n " , nTime - pnode - > nLastSend ) ;
pnode - > fDisconnect = true ;
}
2020-08-14 13:42:15 +02:00
else if ( nTime - pnode - > nLastRecv > TIMEOUT_INTERVAL )
2018-09-24 22:43:00 +02:00
{
LogPrintf ( " socket receive timeout: %is \n " , nTime - pnode - > nLastRecv ) ;
pnode - > fDisconnect = true ;
}
else if ( pnode - > nPingNonceSent & & pnode - > nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros ( ) )
{
LogPrintf ( " ping timeout: %fs \n " , 0.000001 * ( GetTimeMicros ( ) - pnode - > nPingUsecStart ) ) ;
pnode - > fDisconnect = true ;
}
else if ( ! pnode - > fSuccessfullyConnected )
{
LogPrint ( BCLog : : NET , " version handshake timeout from %d \n " , pnode - > GetId ( ) ) ;
pnode - > fDisconnect = true ;
}
}
}
2018-09-25 21:32:07 +02:00
bool CConnman : : GenerateSelectSet ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set )
2018-09-24 22:36:58 +02:00
{
2018-09-24 23:03:17 +02:00
for ( const ListenSocket & hListenSocket : vhListenSocket ) {
2018-09-25 21:32:07 +02:00
recv_set . insert ( hListenSocket . socket ) ;
2018-09-24 23:03:17 +02:00
}
2014-06-24 09:09:45 +02:00
2018-09-24 23:03:17 +02:00
{
LOCK ( cs_vNodes ) ;
for ( CNode * pnode : vNodes )
2010-08-29 18:58:15 +02:00
{
2020-04-07 17:52:20 +02:00
bool select_recv = ! pnode - > fHasRecvData ;
bool select_send = ! pnode - > fCanSendData ;
2017-02-10 12:38:45 +01:00
2018-09-24 23:03:17 +02:00
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket = = INVALID_SOCKET )
continue ;
2017-02-10 12:38:45 +01:00
2018-09-25 21:32:07 +02:00
error_set . insert ( pnode - > hSocket ) ;
2018-09-24 23:03:17 +02:00
if ( select_send ) {
2018-09-25 21:32:07 +02:00
send_set . insert ( pnode - > hSocket ) ;
2018-09-24 23:03:17 +02:00
}
if ( select_recv ) {
2018-09-25 21:32:07 +02:00
recv_set . insert ( pnode - > hSocket ) ;
2010-08-29 18:58:15 +02:00
}
}
2018-09-24 23:03:17 +02:00
}
2010-08-29 18:58:15 +02:00
2020-04-06 07:18:50 +02:00
# ifdef USE_WAKEUP_PIPE
2020-04-03 12:34:14 +02:00
// We add a pipe to the read set so that the select() call can be woken up from the outside
2020-04-17 11:09:34 +02:00
// This is done when data is added to send buffers (vSendMsg) or when new peers are added
2020-04-03 12:34:14 +02:00
// This is currently only implemented for POSIX compliant systems. This means that Windows will fall back to
// timing out after 50ms and then trying to send. This is ok as we assume that heavy-load daemons are usually
// run on Linux and friends.
recv_set . insert ( wakeupPipe [ 0 ] ) ;
# endif
2018-09-25 21:32:07 +02:00
return ! recv_set . empty ( ) | | ! send_set . empty ( ) | | ! error_set . empty ( ) ;
}
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
void CConnman : : SocketEventsKqueue ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set , bool fOnlyPoll )
{
const size_t maxEvents = 64 ;
struct kevent events [ maxEvents ] ;
struct timespec timeout ;
timeout . tv_sec = fOnlyPoll ? 0 : SELECT_TIMEOUT_MILLISECONDS / 1000 ;
timeout . tv_nsec = ( fOnlyPoll ? 0 : SELECT_TIMEOUT_MILLISECONDS % 1000 ) * 1000 * 1000 ;
wakeupSelectNeeded = true ;
int n = kevent ( kqueuefd , nullptr , 0 , events , maxEvents , & timeout ) ;
wakeupSelectNeeded = false ;
if ( n = = - 1 ) {
LogPrintf ( " kevent wait error \n " ) ;
} else if ( n > 0 ) {
for ( int i = 0 ; i < n ; i + + ) {
auto & event = events [ i ] ;
if ( ( event . flags & EV_ERROR ) | | ( event . flags & EV_EOF ) ) {
error_set . insert ( ( SOCKET ) event . ident ) ;
continue ;
}
if ( event . filter = = EVFILT_READ ) {
recv_set . insert ( ( SOCKET ) event . ident ) ;
}
if ( event . filter = = EVFILT_WRITE ) {
send_set . insert ( ( SOCKET ) event . ident ) ;
}
}
}
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
void CConnman : : SocketEventsEpoll ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set , bool fOnlyPoll )
{
const size_t maxEvents = 64 ;
epoll_event events [ maxEvents ] ;
wakeupSelectNeeded = true ;
int n = epoll_wait ( epollfd , events , maxEvents , fOnlyPoll ? 0 : SELECT_TIMEOUT_MILLISECONDS ) ;
wakeupSelectNeeded = false ;
for ( int i = 0 ; i < n ; i + + ) {
auto & e = events [ i ] ;
if ( ( e . events & EPOLLERR ) | | ( e . events & EPOLLHUP ) ) {
error_set . insert ( ( SOCKET ) e . data . fd ) ;
continue ;
}
if ( e . events & EPOLLIN ) {
recv_set . insert ( ( SOCKET ) e . data . fd ) ;
}
if ( e . events & EPOLLOUT ) {
send_set . insert ( ( SOCKET ) e . data . fd ) ;
}
}
}
# endif
2018-09-27 03:54:52 +02:00
# ifdef USE_POLL
2020-04-08 10:28:53 +02:00
void CConnman : : SocketEventsPoll ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set , bool fOnlyPoll )
2018-09-27 03:54:52 +02:00
{
std : : set < SOCKET > recv_select_set , send_select_set , error_select_set ;
if ( ! GenerateSelectSet ( recv_select_set , send_select_set , error_select_set ) ) {
2020-04-08 10:28:53 +02:00
if ( ! fOnlyPoll ) interruptNet . sleep_for ( std : : chrono : : milliseconds ( SELECT_TIMEOUT_MILLISECONDS ) ) ;
2018-09-27 03:54:52 +02:00
return ;
}
std : : unordered_map < SOCKET , struct pollfd > pollfds ;
for ( SOCKET socket_id : recv_select_set ) {
pollfds [ socket_id ] . fd = socket_id ;
pollfds [ socket_id ] . events | = POLLIN ;
}
for ( SOCKET socket_id : send_select_set ) {
pollfds [ socket_id ] . fd = socket_id ;
pollfds [ socket_id ] . events | = POLLOUT ;
}
for ( SOCKET socket_id : error_select_set ) {
pollfds [ socket_id ] . fd = socket_id ;
// These flags are ignored, but we set them for clarity
pollfds [ socket_id ] . events | = POLLERR | POLLHUP ;
}
std : : vector < struct pollfd > vpollfds ;
vpollfds . reserve ( pollfds . size ( ) ) ;
for ( auto it : pollfds ) {
vpollfds . push_back ( std : : move ( it . second ) ) ;
}
2020-04-08 23:19:17 +02:00
wakeupSelectNeeded = true ;
2020-04-08 10:28:53 +02:00
int r = poll ( vpollfds . data ( ) , vpollfds . size ( ) , fOnlyPoll ? 0 : SELECT_TIMEOUT_MILLISECONDS ) ;
2020-04-08 23:19:17 +02:00
wakeupSelectNeeded = false ;
if ( r < 0 ) {
return ;
}
2018-09-27 03:54:52 +02:00
if ( interruptNet ) return ;
for ( struct pollfd pollfd_entry : vpollfds ) {
if ( pollfd_entry . revents & POLLIN ) recv_set . insert ( pollfd_entry . fd ) ;
if ( pollfd_entry . revents & POLLOUT ) send_set . insert ( pollfd_entry . fd ) ;
if ( pollfd_entry . revents & ( POLLERR | POLLHUP ) ) error_set . insert ( pollfd_entry . fd ) ;
}
}
2020-04-16 10:11:26 +02:00
# endif
2020-04-08 10:28:53 +02:00
void CConnman : : SocketEventsSelect ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set , bool fOnlyPoll )
2018-09-25 21:32:07 +02:00
{
std : : set < SOCKET > recv_select_set , send_select_set , error_select_set ;
if ( ! GenerateSelectSet ( recv_select_set , send_select_set , error_select_set ) ) {
interruptNet . sleep_for ( std : : chrono : : milliseconds ( SELECT_TIMEOUT_MILLISECONDS ) ) ;
return ;
}
//
// Find which sockets have data to receive
//
struct timeval timeout ;
timeout . tv_sec = 0 ;
2020-04-08 10:28:53 +02:00
timeout . tv_usec = fOnlyPoll ? 0 : SELECT_TIMEOUT_MILLISECONDS * 1000 ; // frequency to poll pnode->vSend
2018-09-25 21:32:07 +02:00
fd_set fdsetRecv ;
fd_set fdsetSend ;
fd_set fdsetError ;
FD_ZERO ( & fdsetRecv ) ;
FD_ZERO ( & fdsetSend ) ;
FD_ZERO ( & fdsetError ) ;
SOCKET hSocketMax = 0 ;
for ( SOCKET hSocket : recv_select_set ) {
FD_SET ( hSocket , & fdsetRecv ) ;
hSocketMax = std : : max ( hSocketMax , hSocket ) ;
}
for ( SOCKET hSocket : send_select_set ) {
FD_SET ( hSocket , & fdsetSend ) ;
hSocketMax = std : : max ( hSocketMax , hSocket ) ;
}
for ( SOCKET hSocket : error_select_set ) {
FD_SET ( hSocket , & fdsetError ) ;
hSocketMax = std : : max ( hSocketMax , hSocket ) ;
}
2018-09-24 23:03:17 +02:00
wakeupSelectNeeded = true ;
2018-09-25 21:32:07 +02:00
int nSelect = select ( hSocketMax + 1 , & fdsetRecv , & fdsetSend , & fdsetError , & timeout ) ;
2018-09-24 23:03:17 +02:00
wakeupSelectNeeded = false ;
if ( interruptNet )
return ;
2013-03-07 04:31:26 +01:00
2018-09-24 23:03:17 +02:00
if ( nSelect = = SOCKET_ERROR )
{
2018-09-25 21:32:07 +02:00
int nErr = WSAGetLastError ( ) ;
LogPrintf ( " socket select error %s \n " , NetworkErrorString ( nErr ) ) ;
for ( unsigned int i = 0 ; i < = hSocketMax ; i + + )
FD_SET ( i , & fdsetRecv ) ;
2018-09-24 23:03:17 +02:00
FD_ZERO ( & fdsetSend ) ;
FD_ZERO ( & fdsetError ) ;
2018-10-29 21:30:30 +01:00
if ( ! interruptNet . sleep_for ( std : : chrono : : milliseconds ( SELECT_TIMEOUT_MILLISECONDS ) ) )
2018-09-24 23:03:17 +02:00
return ;
}
2010-08-29 18:58:15 +02:00
2018-09-27 03:51:46 +02:00
for ( SOCKET hSocket : recv_select_set ) {
if ( FD_ISSET ( hSocket , & fdsetRecv ) ) {
recv_set . insert ( hSocket ) ;
}
}
for ( SOCKET hSocket : send_select_set ) {
if ( FD_ISSET ( hSocket , & fdsetSend ) ) {
send_set . insert ( hSocket ) ;
}
}
for ( SOCKET hSocket : error_select_set ) {
if ( FD_ISSET ( hSocket , & fdsetError ) ) {
error_set . insert ( hSocket ) ;
}
}
}
2020-04-16 10:11:26 +02:00
2020-04-08 10:28:53 +02:00
void CConnman : : SocketEvents ( std : : set < SOCKET > & recv_set , std : : set < SOCKET > & send_set , std : : set < SOCKET > & error_set , bool fOnlyPoll )
2020-04-16 10:11:26 +02:00
{
switch ( socketEventsMode ) {
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
case SOCKETEVENTS_KQUEUE :
SocketEventsKqueue ( recv_set , send_set , error_set , fOnlyPoll ) ;
break ;
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
case SOCKETEVENTS_EPOLL :
SocketEventsEpoll ( recv_set , send_set , error_set , fOnlyPoll ) ;
break ;
# endif
2020-04-16 10:11:26 +02:00
# ifdef USE_POLL
case SOCKETEVENTS_POLL :
2020-04-08 10:28:53 +02:00
SocketEventsPoll ( recv_set , send_set , error_set , fOnlyPoll ) ;
2020-04-16 10:11:26 +02:00
break ;
2018-09-27 03:54:52 +02:00
# endif
2020-04-16 10:11:26 +02:00
case SOCKETEVENTS_SELECT :
2020-04-08 10:28:53 +02:00
SocketEventsSelect ( recv_set , send_set , error_set , fOnlyPoll ) ;
2020-04-16 10:11:26 +02:00
break ;
default :
assert ( false ) ;
}
}
2018-09-27 03:51:46 +02:00
void CConnman : : SocketHandler ( )
{
2020-04-08 10:28:53 +02:00
bool fOnlyPoll = false ;
{
// check if we have work to do and thus should avoid waiting for events
LOCK2 ( cs_vNodes , cs_mapNodesWithDataToSend ) ;
if ( ! mapReceivableNodes . empty ( ) ) {
fOnlyPoll = true ;
} else if ( ! mapSendableNodes . empty ( ) & & ! mapNodesWithDataToSend . empty ( ) ) {
// we must check if at least one of the nodes with pending messages is also sendable, as otherwise a single
// node would be able to make the network thread busy with polling
for ( auto & p : mapNodesWithDataToSend ) {
if ( mapSendableNodes . count ( p . first ) ) {
fOnlyPoll = true ;
break ;
}
}
}
}
2018-09-27 03:51:46 +02:00
std : : set < SOCKET > recv_set , send_set , error_set ;
2020-04-08 10:28:53 +02:00
SocketEvents ( recv_set , send_set , error_set , fOnlyPoll ) ;
2018-09-27 03:51:46 +02:00
2020-04-06 07:18:50 +02:00
# ifdef USE_WAKEUP_PIPE
2020-04-03 12:34:14 +02:00
// drain the wakeup pipe
if ( recv_set . count ( wakeupPipe [ 0 ] ) ) {
char buf [ 128 ] ;
while ( true ) {
int r = read ( wakeupPipe [ 0 ] , buf , sizeof ( buf ) ) ;
if ( r < = 0 ) {
break ;
}
}
}
# endif
2018-09-27 03:51:46 +02:00
if ( interruptNet ) return ;
2018-09-24 23:03:17 +02:00
//
// Accept new connections
//
for ( const ListenSocket & hListenSocket : vhListenSocket )
{
2023-07-14 15:01:58 +02:00
if ( recv_set . count ( hListenSocket . socket ) > 0 )
2010-08-29 18:58:15 +02:00
{
2018-09-24 23:03:17 +02:00
AcceptConnection ( hListenSocket ) ;
2010-08-29 18:58:15 +02:00
}
2018-09-24 23:03:17 +02:00
}
2020-04-07 17:52:20 +02:00
std : : vector < CNode * > vErrorNodes ;
std : : vector < CNode * > vReceivableNodes ;
std : : vector < CNode * > vSendableNodes ;
2018-09-24 23:03:17 +02:00
{
2020-04-07 17:52:20 +02:00
LOCK ( cs_vNodes ) ;
for ( auto hSocket : error_set ) {
auto it = mapSocketToNode . find ( hSocket ) ;
if ( it = = mapSocketToNode . end ( ) ) {
continue ;
}
it - > second - > AddRef ( ) ;
vErrorNodes . emplace_back ( it - > second ) ;
}
for ( auto hSocket : recv_set ) {
if ( error_set . count ( hSocket ) ) {
// no need to handle it twice
continue ;
}
2010-08-29 18:58:15 +02:00
2020-04-07 17:52:20 +02:00
auto it = mapSocketToNode . find ( hSocket ) ;
if ( it = = mapSocketToNode . end ( ) ) {
2018-09-24 23:03:17 +02:00
continue ;
2020-04-07 17:52:20 +02:00
}
auto jt = mapReceivableNodes . emplace ( it - > second - > GetId ( ) , it - > second ) ;
assert ( jt . first - > second = = it - > second ) ;
it - > second - > fHasRecvData = true ;
2018-09-24 23:03:17 +02:00
}
2020-04-07 17:52:20 +02:00
for ( auto hSocket : send_set ) {
auto it = mapSocketToNode . find ( hSocket ) ;
if ( it = = mapSocketToNode . end ( ) ) {
continue ;
}
auto jt = mapSendableNodes . emplace ( it - > second - > GetId ( ) , it - > second ) ;
assert ( jt . first - > second = = it - > second ) ;
it - > second - > fCanSendData = true ;
2018-09-24 23:03:17 +02:00
}
2010-08-29 18:58:15 +02:00
2020-04-07 17:52:20 +02:00
// collect nodes that have a receivable socket
// also clean up mapReceivableNodes from nodes that were receivable in the last iteration but aren't anymore
vReceivableNodes . reserve ( mapReceivableNodes . size ( ) ) ;
for ( auto it = mapReceivableNodes . begin ( ) ; it ! = mapReceivableNodes . end ( ) ; ) {
if ( ! it - > second - > fHasRecvData ) {
it = mapReceivableNodes . erase ( it ) ;
} else {
// Implement the following logic:
// * If there is data to send, try sending data. As this only
// happens when optimistic write failed, we choose to first drain the
// write buffer in this case before receiving more. This avoids
// needlessly queueing received data, if the remote peer is not themselves
// receiving data. This means properly utilizing TCP flow control signalling.
// * Otherwise, if there is space left in the receive buffer (!fPauseRecv), try
// receiving data (which should succeed as the socket signalled as receivable).
if ( ! it - > second - > fPauseRecv & & it - > second - > nSendMsgSize = = 0 & & ! it - > second - > fDisconnect ) {
it - > second - > AddRef ( ) ;
vReceivableNodes . emplace_back ( it - > second ) ;
}
+ + it ;
}
}
// collect nodes that have data to send and have a socket with non-empty write buffers
// also clean up mapNodesWithDataToSend from nodes that had messages to send in the last iteration
// but don't have any in this iteration
LOCK ( cs_mapNodesWithDataToSend ) ;
vSendableNodes . reserve ( mapNodesWithDataToSend . size ( ) ) ;
for ( auto it = mapNodesWithDataToSend . begin ( ) ; it ! = mapNodesWithDataToSend . end ( ) ; ) {
if ( it - > second - > nSendMsgSize = = 0 ) {
2020-04-17 23:07:17 +02:00
// See comment in PushMessage
it - > second - > Release ( ) ;
2020-04-07 17:52:20 +02:00
it = mapNodesWithDataToSend . erase ( it ) ;
} else {
if ( it - > second - > fCanSendData ) {
it - > second - > AddRef ( ) ;
vSendableNodes . emplace_back ( it - > second ) ;
}
+ + it ;
2018-09-24 23:03:17 +02:00
}
2010-08-29 18:58:15 +02:00
}
2020-04-07 17:52:20 +02:00
}
for ( CNode * pnode : vErrorNodes )
{
if ( interruptNet ) {
2020-04-08 10:46:29 +02:00
break ;
2020-04-07 17:52:20 +02:00
}
// let recv() return errors and then handle it
SocketRecvData ( pnode ) ;
}
for ( CNode * pnode : vReceivableNodes )
{
if ( interruptNet ) {
2020-04-08 10:46:29 +02:00
break ;
2020-04-07 17:52:20 +02:00
}
if ( pnode - > fPauseRecv ) {
continue ;
}
SocketRecvData ( pnode ) ;
}
for ( CNode * pnode : vSendableNodes ) {
if ( interruptNet ) {
2020-04-08 10:46:29 +02:00
break ;
2020-04-07 17:52:20 +02:00
}
2018-09-24 23:03:17 +02:00
2021-01-06 07:07:32 +01:00
// Send data
size_t bytes_sent = WITH_LOCK ( pnode - > cs_vSend , return SocketSendData ( pnode ) ) ;
if ( bytes_sent ) RecordBytesSent ( bytes_sent ) ;
2020-04-07 17:52:20 +02:00
}
ReleaseNodeVector ( vErrorNodes ) ;
ReleaseNodeVector ( vReceivableNodes ) ;
ReleaseNodeVector ( vSendableNodes ) ;
2020-04-08 10:46:29 +02:00
if ( interruptNet ) {
return ;
}
2020-04-07 17:52:20 +02:00
{
LOCK ( cs_vNodes ) ;
// remove nodes from mapSendableNodes, so that the next iteration knows that there is no work to do
// (even if there are pending messages to be sent)
for ( auto it = mapSendableNodes . begin ( ) ; it ! = mapSendableNodes . end ( ) ; ) {
if ( ! it - > second - > fCanSendData ) {
LogPrint ( BCLog : : NET , " %s -- remove mapSendableNodes, peer=%d \n " , __func__ , it - > second - > GetId ( ) ) ;
it = mapSendableNodes . erase ( it ) ;
} else {
+ + it ;
}
}
2018-09-24 23:03:17 +02:00
}
}
2020-04-07 14:27:33 +02:00
size_t CConnman : : SocketRecvData ( CNode * pnode )
{
// typical socket buffer is 8K-64K
2020-11-23 10:25:56 +01:00
uint8_t pchBuf [ 0x10000 ] ;
2020-04-07 14:27:33 +02:00
int nBytes = 0 ;
{
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket = = INVALID_SOCKET )
return 0 ;
2020-11-23 10:25:56 +01:00
nBytes = recv ( pnode - > hSocket , ( char * ) pchBuf , sizeof ( pchBuf ) , MSG_DONTWAIT ) ;
2020-04-07 17:52:20 +02:00
if ( nBytes < ( int ) sizeof ( pchBuf ) ) {
pnode - > fHasRecvData = false ;
}
2020-04-07 14:27:33 +02:00
}
if ( nBytes > 0 )
{
bool notify = false ;
2020-11-23 10:25:56 +01:00
if ( ! pnode - > ReceiveMsgBytes ( Span < const uint8_t > ( pchBuf , nBytes ) , notify ) ) {
2020-04-07 07:00:41 +02:00
LOCK ( cs_vNodes ) ;
pnode - > CloseSocketDisconnect ( this ) ;
}
2020-04-07 14:27:33 +02:00
RecordBytesRecv ( nBytes ) ;
if ( notify ) {
size_t nSizeAdded = 0 ;
auto it ( pnode - > vRecvMsg . begin ( ) ) ;
for ( ; it ! = pnode - > vRecvMsg . end ( ) ; + + it ) {
2019-06-13 10:39:44 +02:00
// vRecvMsg contains only completed CNetMessage
// the single possible partially deserialized message are held by TransportDeserializer
nSizeAdded + = it - > m_raw_message_size ;
2020-04-07 14:27:33 +02:00
}
{
LOCK ( pnode - > cs_vProcessMsg ) ;
pnode - > vProcessMsg . splice ( pnode - > vProcessMsg . end ( ) , pnode - > vRecvMsg , pnode - > vRecvMsg . begin ( ) , it ) ;
pnode - > nProcessQueueSize + = nSizeAdded ;
pnode - > fPauseRecv = pnode - > nProcessQueueSize > nReceiveFloodSize ;
}
WakeMessageHandler ( ) ;
}
}
else if ( nBytes = = 0 )
{
// socket closed gracefully
if ( ! pnode - > fDisconnect ) {
2019-10-25 20:56:10 +02:00
LogPrint ( BCLog : : NET , " socket closed for peer=%d \n " , pnode - > GetId ( ) ) ;
2020-04-07 14:27:33 +02:00
}
2020-04-07 07:00:41 +02:00
LOCK ( cs_vNodes ) ;
2020-04-22 07:50:40 +02:00
pnode - > fOtherSideDisconnected = true ; // avoid lingering
2020-04-07 07:00:41 +02:00
pnode - > CloseSocketDisconnect ( this ) ;
2020-04-07 14:27:33 +02:00
}
else if ( nBytes < 0 )
{
// error
int nErr = WSAGetLastError ( ) ;
if ( nErr ! = WSAEWOULDBLOCK & & nErr ! = WSAEMSGSIZE & & nErr ! = WSAEINTR & & nErr ! = WSAEINPROGRESS )
{
2019-10-25 20:56:10 +02:00
if ( ! pnode - > fDisconnect ) {
LogPrint ( BCLog : : NET , " socket recv error for peer=%d: %s \n " , pnode - > GetId ( ) , NetworkErrorString ( nErr ) ) ;
}
2020-04-07 07:00:41 +02:00
LOCK ( cs_vNodes ) ;
2020-04-22 07:50:40 +02:00
pnode - > fOtherSideDisconnected = true ; // avoid lingering
2020-04-07 07:00:41 +02:00
pnode - > CloseSocketDisconnect ( this ) ;
2020-04-07 14:27:33 +02:00
}
}
if ( nBytes < 0 ) {
return 0 ;
}
return ( size_t ) nBytes ;
}
2018-09-24 23:03:17 +02:00
void CConnman : : ThreadSocketHandler ( )
{
2020-04-07 06:56:25 +02:00
int64_t nLastCleanupNodes = 0 ;
2018-09-24 23:03:17 +02:00
while ( ! interruptNet )
{
2020-04-17 11:09:49 +02:00
// Handle sockets before we do the next round of disconnects. This allows us to flush send buffers one last time
// before actually closing sockets. Receiving is however skipped in case a peer is pending to be disconnected
SocketHandler ( ) ;
2020-04-07 06:56:25 +02:00
if ( GetTimeMillis ( ) - nLastCleanupNodes > 1000 ) {
ForEachNode ( AllNodes , [ & ] ( CNode * pnode ) {
InactivityCheck ( pnode ) ;
} ) ;
nLastCleanupNodes = GetTimeMillis ( ) ;
}
2020-04-22 16:05:11 +02:00
DisconnectNodes ( ) ;
2018-09-24 23:03:17 +02:00
NotifyNumConnectionsChanged ( ) ;
2010-08-29 18:58:15 +02:00
}
}
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
void CConnman : : WakeMessageHandler ( )
{
{
2021-06-09 14:06:31 +02:00
LOCK ( mutexMsgProc ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
fMsgProcWake = true ;
}
condMsgProc . notify_one ( ) ;
}
2010-08-29 18:58:15 +02:00
2019-02-14 18:50:18 +01:00
void CConnman : : WakeSelect ( )
{
2020-04-06 07:18:50 +02:00
# ifdef USE_WAKEUP_PIPE
2019-02-14 18:50:18 +01:00
if ( wakeupPipe [ 1 ] = = - 1 ) {
return ;
}
2020-12-27 02:02:13 +01:00
char buf { 0 } ;
if ( write ( wakeupPipe [ 1 ] , & buf , sizeof ( buf ) ) ! = 1 ) {
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " write to wakeupPipe failed \n " ) ;
2019-02-14 18:50:18 +01:00
}
# endif
2019-04-12 12:58:42 +02:00
wakeupSelectNeeded = false ;
2019-02-14 18:50:18 +01:00
}
2010-08-29 18:58:15 +02:00
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
2017-07-21 11:35:19 +02:00
void CConnman : : ThreadDNSAddressSeed ( )
2011-11-21 18:25:00 +01:00
{
2019-09-23 06:41:32 +02:00
FastRandomContext rng ;
std : : vector < std : : string > seeds = Params ( ) . DNSSeeds ( ) ;
Shuffle ( seeds . begin ( ) , seeds . end ( ) , rng ) ;
int seeds_right_now = 0 ; // Number of seeds left before testing if we have enough connections
int found = 0 ;
2014-07-29 17:04:46 +02:00
2019-09-23 06:41:32 +02:00
if ( gArgs . GetBoolArg ( " -forcednsseed " , DEFAULT_FORCEDNSSEED ) ) {
// When -forcednsseed is provided, query all.
seeds_right_now = seeds . size ( ) ;
2020-02-11 04:20:21 +01:00
} else if ( addrman . size ( ) = = 0 ) {
// If we have no known peers, query all.
2022-10-15 23:51:44 +02:00
// This will occur on the first run, or if peers.dat has been
// deleted.
2020-02-11 04:20:21 +01:00
seeds_right_now = seeds . size ( ) ;
2014-07-29 17:04:46 +02:00
}
2020-02-11 04:20:21 +01:00
// goal: only query DNS seed if address need is acute
// * If we have a reasonable number of peers in addrman, spend
// some time trying them first. This improves user privacy by
// creating fewer identifying DNS requests, reduces trust by
// giving seeds less influence on the network topology, and
// reduces traffic to the seeds.
// * When querying DNS seeds query a few at once, this ensures
// that we don't give DNS seeds the ability to eclipse nodes
// that query them.
// * If we continue having problems, eventually query all the
// DNS seeds, and if that fails too, also try the fixed seeds.
// (done in ThreadOpenConnections)
const std : : chrono : : seconds seeds_wait_time = ( addrman . size ( ) > = DNSSEEDS_DELAY_PEER_THRESHOLD ? DNSSEEDS_DELAY_MANY_PEERS : DNSSEEDS_DELAY_FEW_PEERS ) ;
2019-09-23 06:41:32 +02:00
for ( const std : : string & seed : seeds ) {
2020-02-11 04:20:21 +01:00
if ( seeds_right_now = = 0 ) {
seeds_right_now + = DNSSEEDS_TO_QUERY_AT_ONCE ;
2011-03-09 04:40:50 +01:00
2020-02-11 04:20:21 +01:00
if ( addrman . size ( ) > 0 ) {
LogPrintf ( " Waiting %d seconds before querying DNS seeds. \n " , seeds_wait_time . count ( ) ) ;
std : : chrono : : seconds to_wait = seeds_wait_time ;
while ( to_wait . count ( ) > 0 ) {
2022-10-15 23:51:44 +02:00
// if sleeping for the MANY_PEERS interval, wake up
// early to see if we have enough peers and can stop
// this thread entirely freeing up its resources
2020-02-11 04:20:21 +01:00
std : : chrono : : seconds w = std : : min ( DNSSEEDS_DELAY_FEW_PEERS , to_wait ) ;
if ( ! interruptNet . sleep_for ( w ) ) return ;
to_wait - = w ;
int nRelevant = 0 ;
{
LOCK ( cs_vNodes ) ;
for ( const CNode * pnode : vNodes ) {
nRelevant + = pnode - > fSuccessfullyConnected & & ! pnode - > fFeeler & & ! pnode - > fOneShot & & ! pnode - > m_manual_connection & & ! pnode - > fInbound & & ! pnode - > m_masternode_probe_connection ;
}
}
if ( nRelevant > = 2 ) {
if ( found > 0 ) {
LogPrintf ( " %d addresses found from DNS seeds \n " , found ) ;
LogPrintf ( " P2P peers available. Finished DNS seeding. \n " ) ;
} else {
LogPrintf ( " P2P peers available. Skipped DNS seeding. \n " ) ;
}
return ;
}
}
2019-09-23 06:41:32 +02:00
}
}
2013-01-30 05:13:17 +01:00
2020-02-11 04:20:21 +01:00
if ( interruptNet ) return ;
2022-10-15 23:51:44 +02:00
// hold off on querying seeds if P2P network deactivated
2020-02-11 04:20:21 +01:00
if ( ! fNetworkActive ) {
LogPrintf ( " Waiting for network to be reactivated before querying DNS seeds. \n " ) ;
do {
if ( ! interruptNet . sleep_for ( std : : chrono : : seconds { 1 } ) ) return ;
} while ( ! fNetworkActive ) ;
2017-04-17 14:08:19 +02:00
}
2020-02-11 04:20:21 +01:00
2019-09-23 06:41:32 +02:00
LogPrintf ( " Loading addresses from DNS seed %s \n " , seed ) ;
2013-01-30 05:13:17 +01:00
if ( HaveNameProxy ( ) ) {
2018-01-24 13:01:14 +01:00
AddOneShot ( seed ) ;
2013-01-30 05:13:17 +01:00
} else {
2017-07-12 03:20:12 +02:00
std : : vector < CNetAddr > vIPs ;
std : : vector < CAddress > vAdd ;
2017-10-14 00:25:16 +02:00
ServiceFlags requiredServiceBits = GetDesirableServiceFlags ( NODE_NONE ) ;
2018-01-24 13:01:14 +01:00
std : : string host = strprintf ( " x%x.%s " , requiredServiceBits , seed ) ;
2017-06-24 12:16:41 +02:00
CNetAddr resolveSource ;
if ( ! resolveSource . SetInternal ( host ) ) {
continue ;
}
2018-03-07 17:40:32 +01:00
unsigned int nMaxIPs = 256 ; // Limits number of IPs learned from a DNS seed
Merge #17754: net: Don't allow resolving of std::string with embedded NUL characters. Add tests.
7a046cdc1423963bdcbcf9bb98560af61fa90b37 tests: Avoid using C-style NUL-terminated strings as arguments (practicalswift)
fefb9165f23fe9d10ad092ec31715f906e0d2ee7 tests: Add tests to make sure lookup methods fail on std::string parameters with embedded NUL characters (practicalswift)
9574de86ad703ad942cdd0eca79f48c0d42b102b net: Avoid using C-style NUL-terminated strings as arguments in the netbase interface (practicalswift)
Pull request description:
Don't allow resolving of `std::string`:s with embedded `NUL` characters.
Avoid using C-style `NUL`-terminated strings as arguments in the `netbase` interface
Add tests.
The only place in where C-style `NUL`-terminated strings are actually needed is here:
```diff
+ if (!ValidAsCString(name)) {
+ return false;
+ }
...
- int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
+ int nErr = getaddrinfo(name.c_str(), nullptr, &aiHint, &aiRes);
if (nErr)
return false;
```
Interface changes:
```diff
-bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
+bool LookupHost(const std::string& name, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup);
-bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup);
+bool LookupHost(const std::string& name, CNetAddr& addr, bool fAllowLookup);
-bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup);
+bool Lookup(const std::string& name, CService& addr, int portDefault, bool fAllowLookup);
-bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
+bool Lookup(const std::string& name, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
-bool LookupSubNet(const char *pszName, CSubNet& subnet);
+bool LookupSubNet(const std::string& strSubnet, CSubNet& subnet);
-CService LookupNumeric(const char *pszName, int portDefault = 0);
+CService LookupNumeric(const std::string& name, int portDefault = 0);
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
+bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool& outProxyConnectionFailed);
```
It should be noted that the `ConnectThroughProxy` change (from `bool *outProxyConnectionFailed` to `bool& outProxyConnectionFailed`) has nothing to do with `NUL` handling but I thought it was worth doing when touching this file :)
ACKs for top commit:
EthanHeilman:
ACK 7a046cdc1423963bdcbcf9bb98560af61fa90b37
laanwj:
ACK 7a046cdc1423963bdcbcf9bb98560af61fa90b37
Tree-SHA512: 66556e290db996917b54091acd591df221f72230f6b9f6b167b9195ee870ebef6e26f4cda2f6f54d00e1c362e1743bf56785d0de7cae854e6bf7d26f6caccaba
2020-01-22 20:14:12 +01:00
if ( LookupHost ( host , vIPs , nMaxIPs , true ) ) {
2019-09-23 06:41:32 +02:00
for ( const CNetAddr & ip : vIPs ) {
2013-01-30 05:13:17 +01:00
int nOneDay = 24 * 3600 ;
2016-06-08 17:33:21 +02:00
CAddress addr = CAddress ( CService ( ip , Params ( ) . GetDefaultPort ( ) ) , requiredServiceBits ) ;
2019-09-23 06:41:32 +02:00
addr . nTime = GetTime ( ) - 3 * nOneDay - rng . randrange ( 4 * nOneDay ) ; // use a random age between 3 and 7 days old
2013-01-30 05:13:17 +01:00
vAdd . push_back ( addr ) ;
found + + ;
2011-05-02 15:34:42 +02:00
}
2017-06-24 12:16:41 +02:00
addrman . Add ( vAdd , resolveSource ) ;
2018-01-24 13:01:14 +01:00
} else {
// We now avoid directly using results from DNS Seeds which do not support service bit filtering,
// instead using them as a oneshot to get nodes with our desired service bits.
AddOneShot ( seed ) ;
2017-09-02 22:07:11 +02:00
}
2011-03-09 04:40:50 +01:00
}
2019-09-23 06:41:32 +02:00
- - seeds_right_now ;
2011-03-09 04:40:50 +01:00
}
2013-09-18 12:38:08 +02:00
LogPrintf ( " %d addresses found from DNS seeds \n " , found ) ;
2011-03-09 04:40:50 +01:00
}
2010-08-29 18:58:15 +02:00
2011-11-21 18:25:00 +01:00
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
2017-07-21 11:35:19 +02:00
void CConnman : : DumpAddresses ( )
2012-01-04 23:39:45 +01:00
{
2013-04-13 07:13:08 +02:00
int64_t nStart = GetTimeMillis ( ) ;
2012-05-17 04:11:19 +02:00
2012-01-04 23:39:45 +01:00
CAddrDB adb ;
2012-05-17 04:11:19 +02:00
adb . Write ( addrman ) ;
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " Flushed %d addresses to peers.dat %dms \n " ,
2012-05-17 04:11:19 +02:00
addrman . size ( ) , GetTimeMillis ( ) - nStart ) ;
2012-01-04 23:39:45 +01:00
}
2010-08-29 18:58:15 +02:00
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
2017-07-21 11:35:19 +02:00
void CConnman : : ProcessOneShot ( )
2012-04-24 02:15:00 +02:00
{
2017-07-12 03:20:12 +02:00
std : : string strDest ;
2012-04-24 02:15:00 +02:00
{
LOCK ( cs_vOneShots ) ;
if ( vOneShots . empty ( ) )
return ;
strDest = vOneShots . front ( ) ;
vOneShots . pop_front ( ) ;
}
CAddress addr ;
2012-05-10 18:44:07 +02:00
CSemaphoreGrant grant ( * semOutbound , true ) ;
if ( grant ) {
2018-02-02 09:50:12 +01:00
OpenNetworkConnection ( addr , false , & grant , strDest . c_str ( ) , true ) ;
2012-05-10 18:44:07 +02:00
}
2012-04-24 02:15:00 +02:00
}
2017-11-02 20:13:17 +01:00
bool CConnman : : GetTryNewOutboundPeer ( )
{
return m_try_another_outbound_peer ;
}
void CConnman : : SetTryNewOutboundPeer ( bool flag )
{
m_try_another_outbound_peer = flag ;
LogPrint ( BCLog : : NET , " net: setting try another outbound peer=%s \n " , flag ? " true " : " false " ) ;
}
// Return the number of peers we have over our outbound connection limit
// Exclude peers that are marked for disconnect, or are going to be
// disconnected soon (eg one-shots and feelers)
// Also exclude peers that haven't finished initial connection handshake yet
// (so that we don't decide we're over our desired connection limit, and then
// evict some peer that has finished the handshake)
int CConnman : : GetExtraOutboundCount ( )
{
int nOutbound = 0 ;
{
LOCK ( cs_vNodes ) ;
2018-09-04 15:36:09 +02:00
for ( const CNode * pnode : vNodes ) {
2019-09-26 15:36:54 +02:00
// don't count outbound masternodes
2021-01-14 20:59:18 +01:00
if ( pnode - > m_masternode_connection ) {
2019-09-26 15:36:54 +02:00
continue ;
}
2021-01-14 20:59:18 +01:00
if ( ! pnode - > fInbound & & ! pnode - > m_manual_connection & & ! pnode - > fFeeler & & ! pnode - > fDisconnect & & ! pnode - > fOneShot & & pnode - > fSuccessfullyConnected & & ! pnode - > m_masternode_probe_connection ) {
2017-11-02 20:13:17 +01:00
+ + nOutbound ;
}
}
}
2022-06-19 08:02:28 +02:00
return std : : max ( nOutbound - m_max_outbound_full_relay - m_max_outbound_block_relay , 0 ) ;
2017-11-02 20:13:17 +01:00
}
2017-09-06 01:54:31 +02:00
void CConnman : : ThreadOpenConnections ( const std : : vector < std : : string > connect )
2010-08-29 18:58:15 +02:00
{
2022-07-26 16:02:13 +02:00
FastRandomContext rng ;
2010-08-29 18:58:15 +02:00
// Connect to specific addresses
2017-09-06 01:54:31 +02:00
if ( ! connect . empty ( ) )
2010-08-29 18:58:15 +02:00
{
2013-04-13 07:13:08 +02:00
for ( int64_t nLoop = 0 ; ; nLoop + + )
2010-08-29 18:58:15 +02:00
{
2012-04-24 02:15:00 +02:00
ProcessOneShot ( ) ;
2017-09-06 01:54:31 +02:00
for ( const std : : string & strAddr : connect )
2010-08-29 18:58:15 +02:00
{
2017-07-05 05:45:23 +02:00
CAddress addr ( CService ( ) , NODE_NONE ) ;
2017-10-14 00:25:16 +02:00
OpenNetworkConnection ( addr , false , nullptr , strAddr . c_str ( ) , false , false , true ) ;
2010-08-29 18:58:15 +02:00
for ( int i = 0 ; i < 10 & & i < nLoop ; i + + )
{
2017-08-09 18:06:31 +02:00
if ( ! interruptNet . sleep_for ( std : : chrono : : milliseconds ( 500 ) ) )
return ;
2010-08-29 18:58:15 +02:00
}
}
2017-08-09 18:06:31 +02:00
if ( ! interruptNet . sleep_for ( std : : chrono : : milliseconds ( 500 ) ) )
return ;
2010-08-29 18:58:15 +02:00
}
}
// Initiate network connections
2013-04-13 07:13:08 +02:00
int64_t nStart = GetTime ( ) ;
2017-07-17 12:39:12 +02:00
// Minimum time before next feeler connection (in microseconds).
int64_t nNextFeeler = PoissonNextSend ( nStart * 1000 * 1000 , FEELER_INTERVAL ) ;
2017-08-09 18:06:31 +02:00
while ( ! interruptNet )
2010-08-29 18:58:15 +02:00
{
2012-04-24 02:15:00 +02:00
ProcessOneShot ( ) ;
2017-08-09 18:06:31 +02:00
if ( ! interruptNet . sleep_for ( std : : chrono : : milliseconds ( 500 ) ) )
return ;
2012-02-15 21:17:15 +01:00
2012-05-10 18:44:07 +02:00
CSemaphoreGrant grant ( * semOutbound ) ;
2017-08-09 18:06:31 +02:00
if ( interruptNet )
return ;
2010-08-29 18:58:15 +02:00
2013-05-07 15:16:25 +02:00
// Add seed nodes if DNS seeds are all down (an infrastructure attack?).
2022-10-15 23:51:44 +02:00
// Note that we only do this if we started with an empty peers.dat,
// (in which case we will query DNS seeds immediately) *and* the DNS
// seeds have not returned any results.
2013-05-07 15:16:25 +02:00
if ( addrman . size ( ) = = 0 & & ( GetTime ( ) - nStart > 60 ) ) {
static bool done = false ;
if ( ! done ) {
2013-09-18 12:38:08 +02:00
LogPrintf ( " Adding fixed seed nodes as DNS doesn't seem to be available. \n " ) ;
2017-09-03 15:29:10 +02:00
CNetAddr local ;
2017-06-24 12:16:41 +02:00
local . SetInternal ( " fixedseeds " ) ;
2023-09-10 16:10:08 +02:00
addrman . Add ( ConvertSeeds ( Params ( ) . FixedSeeds ( ) ) , local ) ;
2013-05-07 15:16:25 +02:00
done = true ;
2010-08-29 18:58:15 +02:00
}
}
//
// Choose an address to connect to based on most recently seen
//
CAddress addrConnect ;
2012-07-02 02:23:26 +02:00
// Only connect out to one peer per network group (/16 for IPv4).
2017-12-20 12:45:01 +01:00
// This is only done for mainnet and testnet
2022-06-19 08:02:28 +02:00
int nOutboundFullRelay = 0 ;
int nOutboundBlockRelay = 0 ;
2017-07-12 03:20:12 +02:00
std : : set < std : : vector < unsigned char > > setConnected ;
2017-12-20 12:45:01 +01:00
if ( ! Params ( ) . AllowMultipleAddressesFromGroup ( ) ) {
2012-04-06 18:39:12 +02:00
LOCK ( cs_vNodes ) ;
2018-09-04 15:36:09 +02:00
for ( const CNode * pnode : vNodes ) {
2021-01-14 20:59:18 +01:00
if ( ! pnode - > fInbound & & ! pnode - > m_masternode_connection & & ! pnode - > m_manual_connection ) {
2017-01-06 16:47:05 +01:00
// Netgroups for inbound and addnode peers are not excluded because our goal here
// is to not use multiple of our limited outbound slots on a single netgroup
// but inbound and addnode peers do not use our outbound slots. Inbound peers
// also have the added issue that they're attacker controlled and could be used
// to prevent us from connecting to particular hosts if we used them here.
2021-05-11 17:11:07 +02:00
setConnected . insert ( pnode - > addr . GetGroup ( addrman . m_asmap ) ) ;
2023-04-19 16:57:27 +02:00
if ( ! pnode - > IsAddrRelayPeer ( ) ) {
2022-06-19 08:02:28 +02:00
nOutboundBlockRelay + + ;
} else if ( ! pnode - > fFeeler ) {
nOutboundFullRelay + + ;
}
2012-07-02 02:23:26 +02:00
}
2012-05-10 18:44:07 +02:00
}
2012-04-06 18:39:12 +02:00
}
2010-08-29 18:58:15 +02:00
2020-04-08 22:18:45 +02:00
std : : set < uint256 > setConnectedMasternodes ;
{
LOCK ( cs_vNodes ) ;
for ( CNode * pnode : vNodes ) {
2021-07-26 17:52:52 +02:00
auto verifiedProRegTxHash = pnode - > GetVerifiedProRegTxHash ( ) ;
if ( ! verifiedProRegTxHash . IsNull ( ) ) {
setConnectedMasternodes . emplace ( verifiedProRegTxHash ) ;
2020-04-08 22:18:45 +02:00
}
}
}
2017-07-17 12:39:12 +02:00
// Feeler Connections
//
// Design goals:
// * Increase the number of connectable addresses in the tried table.
//
// Method:
2017-05-31 17:29:29 +02:00
// * Choose a random address from new and attempt to connect to it if we can connect
2017-07-17 12:39:12 +02:00
// successfully it is added to tried.
2017-05-31 17:29:29 +02:00
// * Start attempting feeler connections only after node finishes making outbound
2017-07-17 12:39:12 +02:00
// connections.
// * Only make a feeler connection once every few minutes.
//
bool fFeeler = false ;
2017-11-02 20:13:17 +01:00
2022-06-19 08:02:28 +02:00
if ( nOutboundFullRelay > = m_max_outbound_full_relay & & nOutboundBlockRelay > = m_max_outbound_block_relay & & ! GetTryNewOutboundPeer ( ) ) {
2017-07-17 12:39:12 +02:00
int64_t nTime = GetTimeMicros ( ) ; // The current time right now (in microseconds).
if ( nTime > nNextFeeler ) {
nNextFeeler = PoissonNextSend ( nTime , FEELER_INTERVAL ) ;
fFeeler = true ;
} else {
continue ;
}
}
2011-10-04 05:41:47 +02:00
Merge #9037: net: Add test-before-evict discipline to addrman
e68172ed9 Add test-before-evict discipline to addrman (Ethan Heilman)
Pull request description:
This change implement countermeasures 3 (test-before-evict) suggested in our paper: ["Eclipse Attacks on Bitcoin’s Peer-to-Peer Network"](http://cs-people.bu.edu/heilman/eclipse/).
# Design:
A collision occurs when an address, addr1, is being moved to the tried table from the new table, but maps to a position in the tried table which already contains an address (addr2). The current behavior is that addr1 would evict addr2 from the tried table.
This change ensures that during a collision, addr1 is not inserted into tried but instead inserted into a buffer (setTriedCollisions). The to-be-evicted address, addr2, is then tested by [a feeler connection](https://github.com/bitcoin/bitcoin/pull/8282). If addr2 is found to be online, we remove addr1 from the buffer and addr2 is not evicted, on the other hand if addr2 is found be offline it is replaced by addr1.
An additional small advantage of this change is that, as no more than ten addresses can be in the test buffer at once, and addresses are only cleared one at a time from the test buffer (at 2 minute intervals), thus an attacker is forced to wait at least two minutes to insert a new address into tried after filling up the test buffer. This rate limits an attacker attempting to launch an eclipse attack.
# Risk mitigation:
- To prevent this functionality from being used as a DoS vector, we limit the number of addresses which are to be tested to ten. If we have more than ten addresses to test, we drop new addresses being added to tried if they would evict an address. Since the feeler thread only creates one new connection every 2 minutes the additional network overhead is limited.
- An address in tried gains immunity from tests for 4 hours after it has been tested or successfully connected to.
# Tests:
This change includes additional addrman unittests which test this behavior.
I ran an instance of this change with a much smaller tried table (2 buckets of 64 addresses) so that collisions were much more likely and observed evictions.
```
2016-10-27 07:20:26 Swapping 208.12.64.252:8333 for 68.62.95.247:8333 in tried table
2016-10-27 07:20:26 Moving 208.12.64.252:8333 to tried
```
I documented tests we ran against similar earlier versions of this change in #6355.
# Security Benefit
This is was originally posted in PR #8282 see [this comment for full details](https://github.com/bitcoin/bitcoin/pull/8282#issuecomment-237255215).
To determine the security benefit of these larger numbers of IPs in the tried table I modeled the attack presented in [Eclipse Attacks on Bitcoin’s Peer-to-Peer Network](https://eprint.iacr.org/2015/263).
![attackergraph40000-10-1000short-line](https://cloud.githubusercontent.com/assets/274814/17366828/372af458-595b-11e6-81e5-2c9f97282305.png)
**Default node:** 595 attacker IPs for ~50% attack success.
**Default node + test-before-evict:** 620 attacker IPs for ~50% attack success.
**Feeler node:** 5540 attacker IPs for ~50% attack success.
**Feeler node + test-before-evict:** 8600 attacker IPs for ~50% attack success.
The node running feeler connections has 10 times as many online IP addresses in its tried table making an attack 10 times harder (i.e. requiring the an attacker require 10 times as many IP addresses in different /16s). Adding test-before-evict increases resistance of the node by an additional 3000 attacker IP addresses.
Below I graph the attack over even greater attacker resources (i.e. more attacker controled IP addresses). Note that test-before-evict maintains some security far longer even against an attacker with 50,000 IPs. If this node had a larger tried table test-before-evict could greatly boost a nodes resistance to eclipse attacks.
![attacker graph long view](https://cloud.githubusercontent.com/assets/274814/17367108/96f46d64-595c-11e6-91cd-edba160598e7.png)
Tree-SHA512: fdad4d26aadeaad9bcdc71929b3eb4e1f855b3ee3541fbfbe25dca8d7d0a1667815402db0cb4319db6bd3fcd32d67b5bbc0e12045c4252d62d6239b7d77c4395
2018-03-06 21:36:48 +01:00
addrman . ResolveCollisions ( ) ;
2019-04-10 18:14:20 +02:00
auto mnList = deterministicMNManager - > GetListAtChainTip ( ) ;
2017-07-17 12:39:12 +02:00
int64_t nANow = GetAdjustedTime ( ) ;
2012-01-04 23:39:45 +01:00
int nTries = 0 ;
2017-08-09 18:06:31 +02:00
while ( ! interruptNet )
2010-08-29 18:58:15 +02:00
{
2023-09-10 19:02:53 +02:00
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
// already-connected network ranges, ...) before trying new addrman addresses.
nTries + + ;
if ( nTries > 100 )
break ;
Merge #9037: net: Add test-before-evict discipline to addrman
e68172ed9 Add test-before-evict discipline to addrman (Ethan Heilman)
Pull request description:
This change implement countermeasures 3 (test-before-evict) suggested in our paper: ["Eclipse Attacks on Bitcoin’s Peer-to-Peer Network"](http://cs-people.bu.edu/heilman/eclipse/).
# Design:
A collision occurs when an address, addr1, is being moved to the tried table from the new table, but maps to a position in the tried table which already contains an address (addr2). The current behavior is that addr1 would evict addr2 from the tried table.
This change ensures that during a collision, addr1 is not inserted into tried but instead inserted into a buffer (setTriedCollisions). The to-be-evicted address, addr2, is then tested by [a feeler connection](https://github.com/bitcoin/bitcoin/pull/8282). If addr2 is found to be online, we remove addr1 from the buffer and addr2 is not evicted, on the other hand if addr2 is found be offline it is replaced by addr1.
An additional small advantage of this change is that, as no more than ten addresses can be in the test buffer at once, and addresses are only cleared one at a time from the test buffer (at 2 minute intervals), thus an attacker is forced to wait at least two minutes to insert a new address into tried after filling up the test buffer. This rate limits an attacker attempting to launch an eclipse attack.
# Risk mitigation:
- To prevent this functionality from being used as a DoS vector, we limit the number of addresses which are to be tested to ten. If we have more than ten addresses to test, we drop new addresses being added to tried if they would evict an address. Since the feeler thread only creates one new connection every 2 minutes the additional network overhead is limited.
- An address in tried gains immunity from tests for 4 hours after it has been tested or successfully connected to.
# Tests:
This change includes additional addrman unittests which test this behavior.
I ran an instance of this change with a much smaller tried table (2 buckets of 64 addresses) so that collisions were much more likely and observed evictions.
```
2016-10-27 07:20:26 Swapping 208.12.64.252:8333 for 68.62.95.247:8333 in tried table
2016-10-27 07:20:26 Moving 208.12.64.252:8333 to tried
```
I documented tests we ran against similar earlier versions of this change in #6355.
# Security Benefit
This is was originally posted in PR #8282 see [this comment for full details](https://github.com/bitcoin/bitcoin/pull/8282#issuecomment-237255215).
To determine the security benefit of these larger numbers of IPs in the tried table I modeled the attack presented in [Eclipse Attacks on Bitcoin’s Peer-to-Peer Network](https://eprint.iacr.org/2015/263).
![attackergraph40000-10-1000short-line](https://cloud.githubusercontent.com/assets/274814/17366828/372af458-595b-11e6-81e5-2c9f97282305.png)
**Default node:** 595 attacker IPs for ~50% attack success.
**Default node + test-before-evict:** 620 attacker IPs for ~50% attack success.
**Feeler node:** 5540 attacker IPs for ~50% attack success.
**Feeler node + test-before-evict:** 8600 attacker IPs for ~50% attack success.
The node running feeler connections has 10 times as many online IP addresses in its tried table making an attack 10 times harder (i.e. requiring the an attacker require 10 times as many IP addresses in different /16s). Adding test-before-evict increases resistance of the node by an additional 3000 attacker IP addresses.
Below I graph the attack over even greater attacker resources (i.e. more attacker controled IP addresses). Note that test-before-evict maintains some security far longer even against an attacker with 50,000 IPs. If this node had a larger tried table test-before-evict could greatly boost a nodes resistance to eclipse attacks.
![attacker graph long view](https://cloud.githubusercontent.com/assets/274814/17367108/96f46d64-595c-11e6-91cd-edba160598e7.png)
Tree-SHA512: fdad4d26aadeaad9bcdc71929b3eb4e1f855b3ee3541fbfbe25dca8d7d0a1667815402db0cb4319db6bd3fcd32d67b5bbc0e12045c4252d62d6239b7d77c4395
2018-03-06 21:36:48 +01:00
CAddrInfo addr = addrman . SelectTriedCollision ( ) ;
// SelectTriedCollision returns an invalid address if it is empty.
if ( ! fFeeler | | ! addr . IsValid ( ) ) {
addr = addrman . Select ( fFeeler ) ;
}
2010-08-29 18:58:15 +02:00
2020-04-08 22:18:45 +02:00
auto dmn = mnList . GetMNByService ( addr ) ;
bool isMasternode = dmn ! = nullptr ;
2019-04-10 18:14:20 +02:00
2019-03-09 07:08:51 +01:00
// Require outbound connections, other than feelers, to be to distinct network groups
if ( ! fFeeler & & setConnected . count ( addr . GetGroup ( addrman . m_asmap ) ) ) {
break ;
}
2012-01-04 23:39:45 +01:00
// if we selected an invalid address, restart
2021-05-11 17:11:07 +02:00
if ( ! addr . IsValid ( ) | | setConnected . count ( addr . GetGroup ( addrman . m_asmap ) ) )
2018-07-07 23:19:33 +02:00
break ;
2020-04-08 22:18:45 +02:00
// don't try to connect to masternodes that we already have a connection to (most likely inbound)
if ( isMasternode & & setConnectedMasternodes . count ( dmn - > proTxHash ) )
break ;
2018-07-07 23:19:33 +02:00
// if we selected a local address, restart (local addresses are allowed in regtest and devnet)
bool fAllowLocal = Params ( ) . AllowMultiplePorts ( ) & & addrConnect . GetPort ( ) ! = GetListenPort ( ) ;
2019-03-09 07:08:51 +01:00
if ( ! fAllowLocal & & IsLocal ( addrConnect ) ) {
2012-01-04 23:39:45 +01:00
break ;
2019-03-09 07:08:51 +01:00
}
2010-08-29 18:58:15 +02:00
2019-01-14 14:22:47 +01:00
if ( ! IsReachable ( addr ) )
2012-05-04 16:46:22 +02:00
continue ;
2012-01-04 23:39:45 +01:00
// only consider very recently tried nodes after 30 failed attempts
if ( nANow - addr . nLastTry < 600 & & nTries < 30 )
continue ;
2017-10-14 00:25:16 +02:00
// for non-feelers, require all the services we'll want,
// for feelers, only require they be a full node (only because most
// SPV clients don't have a good address DB available)
if ( ! isMasternode & & ! fFeeler & & ! HasAllDesirableServiceFlags ( addr . nServices ) ) {
continue ;
} else if ( ! isMasternode & & fFeeler & & ! MayHaveUsefulAddressDB ( addr . nServices ) ) {
2018-01-25 18:16:40 +01:00
continue ;
2017-06-01 13:20:56 +02:00
}
2018-01-25 18:16:40 +01:00
2020-12-17 12:05:55 +01:00
// Do not allow non-default ports, unless after 50 invalid
// addresses selected already. This is to prevent malicious peers
// from advertising themselves as a service on another host and
// port, causing a DoS attack as nodes around the network attempt
// to connect to it fruitlessly.
2023-07-14 17:22:44 +02:00
if ( ( ! isMasternode | | ! Params ( ) . AllowMultiplePorts ( ) ) & & addr . GetPort ( ) ! = Params ( ) . GetDefaultPort ( addr . GetNetwork ( ) ) & & addr . GetPort ( ) ! = GetListenPort ( ) & & nTries < 50 ) {
2012-01-04 23:39:45 +01:00
continue ;
2023-07-14 17:22:44 +02:00
}
2012-01-04 23:39:45 +01:00
addrConnect = addr ;
break ;
2010-08-29 18:58:15 +02:00
}
2017-07-17 12:39:12 +02:00
if ( addrConnect . IsValid ( ) ) {
if ( fFeeler ) {
// Add small amount of random noise before connection to avoid synchronization.
2022-07-26 16:02:13 +02:00
if ( ! interruptNet . sleep_for ( rng . rand_uniform_duration < CThreadInterrupt : : Clock > ( FEELER_SLEEP_WINDOW ) ) ) {
2017-08-09 18:06:31 +02:00
return ;
2022-07-26 16:02:13 +02:00
}
2019-09-22 22:48:15 +02:00
if ( fLogIPs ) {
LogPrint ( BCLog : : NET , " Making feeler connection to %s \n " , addrConnect . ToString ( ) ) ;
} else {
LogPrint ( BCLog : : NET , " Making feeler connection \n " ) ;
}
2017-07-17 12:39:12 +02:00
}
2022-06-19 08:02:28 +02:00
// Open this connection as block-relay-only if we're already at our
// full-relay capacity, but not yet at our block-relay peer limit.
// (It should not be possible for fFeeler to be set if we're not
// also at our block-relay peer limit, but check against that as
// well for sanity.)
bool block_relay_only = nOutboundBlockRelay < m_max_outbound_block_relay & & ! fFeeler & & nOutboundFullRelay > = m_max_outbound_full_relay ;
OpenNetworkConnection ( addrConnect , ( int ) setConnected . size ( ) > = std : : min ( nMaxConnections - 1 , 2 ) , & grant , nullptr , false , fFeeler , false , block_relay_only ) ;
2017-07-17 12:39:12 +02:00
}
2010-08-29 18:58:15 +02:00
}
}
2023-09-10 19:02:53 +02:00
std : : vector < CAddress > CConnman : : GetCurrentBlockRelayOnlyConns ( ) const
{
std : : vector < CAddress > ret ;
LOCK ( cs_vNodes ) ;
for ( const CNode * pnode : vNodes ) {
if ( pnode - > IsBlockRelayOnly ( ) ) {
ret . push_back ( pnode - > addr ) ;
}
}
return ret ;
}
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
2017-07-21 11:35:19 +02:00
std : : vector < AddedNodeInfo > CConnman : : GetAddedNodeInfo ( )
2011-12-17 01:48:03 +01:00
{
2017-07-14 18:58:57 +02:00
std : : vector < AddedNodeInfo > ret ;
std : : list < std : : string > lAddresses ( 0 ) ;
2012-07-02 19:55:16 +02:00
{
LOCK ( cs_vAddedNodes ) ;
2017-07-14 18:58:57 +02:00
ret . reserve ( vAddedNodes . size ( ) ) ;
2017-09-21 01:32:56 +02:00
std : : copy ( vAddedNodes . cbegin ( ) , vAddedNodes . cend ( ) , std : : back_inserter ( lAddresses ) ) ;
2012-07-02 19:55:16 +02:00
}
2011-12-17 01:48:03 +01:00
2017-07-14 18:58:57 +02:00
// Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService
std : : map < CService , bool > mapConnected ;
std : : map < std : : string , std : : pair < bool , CService > > mapConnectedByName ;
{
LOCK ( cs_vNodes ) ;
for ( const CNode * pnode : vNodes ) {
if ( pnode - > addr . IsValid ( ) ) {
mapConnected [ pnode - > addr ] = pnode - > fInbound ;
2012-07-02 19:55:16 +02:00
}
2017-02-11 00:53:31 +01:00
std : : string addrName = pnode - > GetAddrName ( ) ;
if ( ! addrName . empty ( ) ) {
mapConnectedByName [ std : : move ( addrName ) ] = std : : make_pair ( pnode - > fInbound , static_cast < const CService & > ( pnode - > addr ) ) ;
2017-07-14 18:58:57 +02:00
}
}
}
2019-07-05 09:06:28 +02:00
for ( const std : : string & strAddNode : lAddresses ) {
2023-07-14 17:22:44 +02:00
CService service ( LookupNumeric ( strAddNode , Params ( ) . GetDefaultPort ( strAddNode ) ) ) ;
2018-04-19 14:24:26 +02:00
AddedNodeInfo addedNode { strAddNode , CService ( ) , false , false } ;
2017-07-14 18:58:57 +02:00
if ( service . IsValid ( ) ) {
// strAddNode is an IP:port
auto it = mapConnected . find ( service ) ;
if ( it ! = mapConnected . end ( ) ) {
2018-04-19 14:24:26 +02:00
addedNode . resolvedAddress = service ;
addedNode . fConnected = true ;
addedNode . fInbound = it - > second ;
2017-07-14 18:58:57 +02:00
}
} else {
// strAddNode is a name
auto it = mapConnectedByName . find ( strAddNode ) ;
if ( it ! = mapConnectedByName . end ( ) ) {
2018-04-19 14:24:26 +02:00
addedNode . resolvedAddress = it - > second . second ;
addedNode . fConnected = true ;
addedNode . fInbound = it - > second . first ;
2012-04-19 17:38:03 +02:00
}
}
2018-04-19 14:24:26 +02:00
ret . emplace_back ( std : : move ( addedNode ) ) ;
2012-04-19 17:38:03 +02:00
}
2017-07-14 18:58:57 +02:00
return ret ;
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : ThreadOpenAddedConnections ( )
2017-07-14 18:58:57 +02:00
{
2017-01-06 16:47:05 +01:00
while ( true )
2011-12-17 01:48:03 +01:00
{
2017-01-06 16:47:05 +01:00
CSemaphoreGrant grant ( * semAddnode ) ;
2017-07-14 18:58:57 +02:00
std : : vector < AddedNodeInfo > vInfo = GetAddedNodeInfo ( ) ;
2017-01-06 16:47:05 +01:00
bool tried = false ;
2017-07-14 18:58:57 +02:00
for ( const AddedNodeInfo & info : vInfo ) {
if ( ! info . fConnected ) {
2017-01-06 16:47:05 +01:00
if ( ! grant . TryAcquire ( ) ) {
2018-03-21 16:16:28 +01:00
// If we've used up our semaphore and need a new one, let's not wait here since while we are waiting
2017-01-06 16:47:05 +01:00
// the addednodeinfo state might change.
break ;
}
tried = true ;
2017-09-28 17:02:53 +02:00
CAddress addr ( CService ( ) , NODE_NONE ) ;
2023-04-19 16:57:27 +02:00
OpenNetworkConnection ( addr , false , & grant , info . strAddedNode . c_str ( ) , false , false , true ) ;
2017-08-09 18:06:31 +02:00
if ( ! interruptNet . sleep_for ( std : : chrono : : milliseconds ( 500 ) ) )
return ;
2017-07-14 18:58:57 +02:00
}
2012-07-02 19:55:16 +02:00
}
2017-01-06 16:47:05 +01:00
// Retry every 60 seconds if a connection was attempted, otherwise two seconds
if ( ! interruptNet . sleep_for ( std : : chrono : : seconds ( tried ? 60 : 2 ) ) )
2017-08-09 18:06:31 +02:00
return ;
2011-12-17 01:48:03 +01:00
}
}
2018-02-01 02:10:52 +01:00
void CConnman : : ThreadOpenMasternodeConnections ( )
2017-01-21 20:03:55 +01:00
{
// Connecting to specific addresses, no masternode connections available
2019-06-24 18:44:27 +02:00
if ( gArgs . IsArgSet ( " -connect " ) & & gArgs . GetArgs ( " -connect " ) . size ( ) > 0 )
2017-01-21 20:03:55 +01:00
return ;
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
assert ( : : mmetaman ! = nullptr ) ;
2020-03-21 12:21:09 +01:00
auto & chainParams = Params ( ) ;
2020-03-26 13:24:06 +01:00
bool didConnect = false ;
2017-08-09 18:06:31 +02:00
while ( ! interruptNet )
2017-01-21 20:03:55 +01:00
{
2022-10-12 21:42:51 +02:00
auto sleepTime = std : : chrono : : milliseconds ( 1000 ) ;
2020-03-26 13:24:06 +01:00
if ( didConnect ) {
2022-10-12 21:42:51 +02:00
sleepTime = std : : chrono : : milliseconds ( 100 ) ;
2020-03-26 13:24:06 +01:00
}
2022-10-12 21:42:51 +02:00
if ( ! interruptNet . sleep_for ( sleepTime ) )
2017-08-09 18:06:31 +02:00
return ;
2017-01-21 20:03:55 +01:00
2020-03-26 13:24:06 +01:00
didConnect = false ;
refactor: begin to de-globalize masternodeSync (#5103)
<!--
*** Please remove the following help text before submitting: ***
Provide a general summary of your changes in the Title above
Pull requests without a rationale and clear improvement may be closed
immediately.
Please provide clear motivation for your patch and explain how it
improves
Dash Core user experience or Dash Core developer experience
significantly:
* Any test improvements or new tests that improve coverage are always
welcome.
* All other changes should have accompanying unit tests (see
`src/test/`) or
functional tests (see `test/`). Contributors should note which tests
cover
modified code. If no tests exist for a region of modified code, new
tests
should accompany the change.
* Bug fixes are most welcome when they come with steps to reproduce or
an
explanation of the potential issue as well as reasoning for the way the
bug
was fixed.
* Features are welcome, but might be rejected due to design or scope
issues.
If a feature is based on a lot of dependencies, contributors should
first
consider building the system outside of Dash Core, if possible.
-->
## Issue being fixed or feature implemented
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
minimizing global uses
## What was done?
<!--- Describe your changes in detail -->
Started the deglobalization, a future PR should be done to continue this
deglobalization
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
## Breaking Changes
<!--- Please describe any breaking changes your code introduces -->
none
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation
**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2023-01-04 21:37:20 +01:00
if ( ! fNetworkActive | | ! : : masternodeSync - > IsBlockchainSynced ( ) )
2020-03-26 08:38:12 +01:00
continue ;
2018-05-24 16:15:52 +02:00
std : : set < CService > connectedNodes ;
2022-10-12 21:42:51 +02:00
std : : map < uint256 /*proTxHash*/ , bool /*fInbound*/ > connectedProRegTxHashes ;
2019-03-22 11:52:37 +01:00
ForEachNode ( [ & ] ( const CNode * pnode ) {
2021-07-26 17:52:52 +02:00
auto verifiedProRegTxHash = pnode - > GetVerifiedProRegTxHash ( ) ;
2018-05-24 16:15:52 +02:00
connectedNodes . emplace ( pnode - > addr ) ;
2021-07-26 17:52:52 +02:00
if ( ! verifiedProRegTxHash . IsNull ( ) ) {
connectedProRegTxHashes . emplace ( verifiedProRegTxHash , pnode - > fInbound ) ;
2019-03-22 11:52:37 +01:00
}
2018-05-24 16:15:52 +02:00
} ) ;
2019-04-09 09:13:31 +02:00
auto mnList = deterministicMNManager - > GetListAtChainTip ( ) ;
2017-08-09 18:06:31 +02:00
if ( interruptNet )
return ;
2017-01-21 20:03:55 +01:00
2023-10-23 17:39:39 +02:00
int64_t nANow = GetTime < std : : chrono : : seconds > ( ) . count ( ) ;
2022-07-25 23:36:17 +02:00
constexpr const auto & _func_ = __func__ ;
2019-06-14 13:04:19 +02:00
2018-05-26 20:03:49 +02:00
// NOTE: Process only one pending masternode at a time
2022-06-19 08:02:28 +02:00
MasternodeProbeConn isProbe = MasternodeProbeConn : : IsNotConnection ;
2018-05-24 16:15:52 +02:00
2022-10-12 21:42:51 +02:00
const auto getPendingQuorumNodes = [ & ] ( ) {
LockAssertion lock ( cs_vPendingMasternodes ) ;
std : : vector < CDeterministicMNCPtr > ret ;
for ( const auto & group : masternodeQuorumNodes ) {
for ( const auto & proRegTxHash : group . second ) {
auto dmn = mnList . GetMN ( proRegTxHash ) ;
if ( ! dmn ) {
continue ;
}
const auto & addr2 = dmn - > pdmnState - > addr ;
if ( connectedNodes . count ( addr2 ) & & ! connectedProRegTxHashes . count ( proRegTxHash ) ) {
2023-03-29 18:24:16 +02:00
// we probably connected to it before it became a masternode
// or maybe we are still waiting for mnauth
( void ) ForNode ( addr2 , [ & ] ( CNode * pnode ) {
if ( pnode - > nTimeFirstMessageReceived ! = 0 & & GetSystemTimeInSeconds ( ) - pnode - > nTimeFirstMessageReceived > 5 ) {
// clearly not expecting mnauth to take that long even if it wasn't the first message
// we received (as it should normally), disconnect
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- dropping non-mnauth connection to %s, service=%s \n " , _func_ , proRegTxHash . ToString ( ) , addr2 . ToString ( false ) ) ;
pnode - > fDisconnect = true ;
return true ;
2020-03-17 08:36:45 +01:00
}
2023-03-29 18:24:16 +02:00
return false ;
} ) ;
// either way - it's not ready, skip it for now
continue ;
}
if ( ! connectedNodes . count ( addr2 ) & & ! IsMasternodeOrDisconnectRequested ( addr2 ) & & ! connectedProRegTxHashes . count ( proRegTxHash ) ) {
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
int64_t lastAttempt = mmetaman - > GetMetaInfo ( dmn - > proTxHash ) - > GetLastOutboundAttempt ( ) ;
2023-03-29 18:24:16 +02:00
// back off trying connecting to an address if we already tried recently
if ( nANow - lastAttempt < chainParams . LLMQConnectionRetryTimeout ( ) ) {
continue ;
2020-03-17 08:36:45 +01:00
}
2023-03-29 18:24:16 +02:00
ret . emplace_back ( dmn ) ;
2018-05-24 16:15:52 +02:00
}
}
2023-03-29 18:24:16 +02:00
}
2022-10-12 21:42:51 +02:00
return ret ;
} ;
const auto getPendingProbes = [ & ] ( ) {
LockAssertion lock ( cs_vPendingMasternodes ) ;
std : : vector < CDeterministicMNCPtr > ret ;
for ( auto it = masternodePendingProbes . begin ( ) ; it ! = masternodePendingProbes . end ( ) ; ) {
auto dmn = mnList . GetMN ( * it ) ;
if ( ! dmn ) {
it = masternodePendingProbes . erase ( it ) ;
continue ;
}
bool connectedAndOutbound = connectedProRegTxHashes . count ( dmn - > proTxHash ) & & ! connectedProRegTxHashes [ dmn - > proTxHash ] ;
if ( connectedAndOutbound ) {
// we already have an outbound connection to this MN so there is no theed to probe it again
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
mmetaman - > GetMetaInfo ( dmn - > proTxHash ) - > SetLastOutboundSuccess ( nANow ) ;
2022-10-12 21:42:51 +02:00
it = masternodePendingProbes . erase ( it ) ;
continue ;
}
2018-05-24 16:15:52 +02:00
2022-10-12 21:42:51 +02:00
+ + it ;
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
int64_t lastAttempt = mmetaman - > GetMetaInfo ( dmn - > proTxHash ) - > GetLastOutboundAttempt ( ) ;
2022-10-12 21:42:51 +02:00
// back off trying connecting to an address if we already tried recently
if ( nANow - lastAttempt < chainParams . LLMQConnectionRetryTimeout ( ) ) {
continue ;
2018-05-24 16:15:52 +02:00
}
2022-10-12 21:42:51 +02:00
ret . emplace_back ( dmn ) ;
2018-05-24 16:15:52 +02:00
}
2022-10-12 21:42:51 +02:00
return ret ;
} ;
2020-03-17 10:04:31 +01:00
2022-10-12 21:42:51 +02:00
auto getConnectToDmn = [ & ] ( ) - > CDeterministicMNCPtr {
// don't hold lock while calling OpenMasternodeConnection as cs_main is locked deep inside
LOCK2 ( cs_vNodes , cs_vPendingMasternodes ) ;
2020-03-17 10:04:31 +01:00
2022-10-12 21:42:51 +02:00
if ( ! vPendingMasternodes . empty ( ) ) {
auto dmn = mnList . GetValidMN ( vPendingMasternodes . front ( ) ) ;
vPendingMasternodes . erase ( vPendingMasternodes . begin ( ) ) ;
if ( dmn & & ! connectedNodes . count ( dmn - > pdmnState - > addr ) & & ! IsMasternodeOrDisconnectRequested ( dmn - > pdmnState - > addr ) ) {
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- opening pending masternode connection to %s, service=%s \n " , _func_ , dmn - > proTxHash . ToString ( ) , dmn - > pdmnState - > addr . ToString ( false ) ) ;
return dmn ;
2020-03-17 10:04:31 +01:00
}
2022-10-12 21:42:51 +02:00
}
if ( const auto pending = getPendingQuorumNodes ( ) ; ! pending . empty ( ) ) {
// not-null
auto dmn = pending [ GetRand ( pending . size ( ) ) ] ;
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- opening quorum connection to %s, service=%s \n " ,
_func_ , dmn - > proTxHash . ToString ( ) , dmn - > pdmnState - > addr . ToString ( false ) ) ;
return dmn ;
}
2020-03-17 10:04:31 +01:00
2022-10-12 21:42:51 +02:00
if ( const auto pending = getPendingProbes ( ) ; ! pending . empty ( ) ) {
// not-null
auto dmn = pending [ GetRand ( pending . size ( ) ) ] ;
masternodePendingProbes . erase ( dmn - > proTxHash ) ;
isProbe = MasternodeProbeConn : : IsConnection ;
2020-03-17 10:04:31 +01:00
2022-10-12 21:42:51 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- probing masternode %s, service=%s \n " , _func_ , dmn - > proTxHash . ToString ( ) , dmn - > pdmnState - > addr . ToString ( false ) ) ;
return dmn ;
2020-03-17 10:04:31 +01:00
}
2022-10-12 21:42:51 +02:00
return nullptr ;
} ;
CDeterministicMNCPtr connectToDmn = getConnectToDmn ( ) ;
2018-05-24 16:15:52 +02:00
2022-10-12 21:42:51 +02:00
if ( connectToDmn = = nullptr ) {
2020-03-17 08:36:45 +01:00
continue ;
2017-01-31 16:29:27 +01:00
}
2018-05-26 20:03:49 +02:00
2020-03-26 13:24:06 +01:00
didConnect = true ;
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
mmetaman - > GetMetaInfo ( connectToDmn - > proTxHash ) - > SetLastOutboundAttempt ( nANow ) ;
2020-03-19 13:51:25 +01:00
2020-03-17 10:04:31 +01:00
OpenMasternodeConnection ( CAddress ( connectToDmn - > pdmnState - > addr , NODE_NETWORK ) , isProbe ) ;
2018-05-26 20:03:49 +02:00
// should be in the list now if connection was opened
2020-03-17 10:04:31 +01:00
bool connected = ForNode ( connectToDmn - > pdmnState - > addr , CConnman : : AllNodes , [ & ] ( CNode * pnode ) {
2018-05-26 20:03:49 +02:00
if ( pnode - > fDisconnect ) {
return false ;
}
return true ;
} ) ;
2020-03-17 10:04:31 +01:00
if ( ! connected ) {
2020-04-16 09:38:33 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- connection failed for masternode %s, service=%s \n " , __func__ , connectToDmn - > proTxHash . ToString ( ) , connectToDmn - > pdmnState - > addr . ToString ( false ) ) ;
2022-07-07 09:49:34 +02:00
// Will take a few consequent failed attempts to PoSe-punish a MN.
refactor: decouple db hooks from CFlatDB-based C*Manager objects, migrate to *Store structs (#5555)
## Motivation
As highlighted in https://github.com/dashpay/dash-issues/issues/52,
decoupling of `CFlatDB`-interacting components from managers of objects
like `CGovernanceManager` and `CSporkManager` is a key task for
achieving deglobalization of Dash-specific components.
The design of `CFlatDB` as a flat database agent relies on hooking into
the object's state its meant to load and store, using its
(de)serialization routines and other miscellaneous functions (notably,
without defining an interface) to achieve those ends. This approach was
taken predominantly for components that want a single-file cache.
Because of the method it uses to hook into the object (templates and the
use of temporary objects), it explicitly prevented passing arguments
into the object constructor, an explicit requirement for storing
references to other components during construction. This, in turn,
created an explicit dependency on those same components being available
in the global context, which would block the backport of bitcoin#21866,
a requirement for future backports meant to achieve parity in
`assumeutxo` support.
The design of these objects made no separation between persistent (i.e.
cached) and ephemeral (i.e. generated/fetched during initialization or
state transitions) data and the design of `CFlatDB` attempts to "clean"
the database by breaching this separation and attempting to access this
ephemeral data.
This might be acceptable if it is contained within the manager itself,
like `CSporkManager`'s `CheckAndRemove()` but is utterly unacceptable
when it relies on other managers (that, as a reminder, are only
accessible through the global state because of restrictions caused by
existing design), like `CGovernanceManager`'s `UpdateCachesAndClean()`.
This pull request aims to separate the `CFlatDB`-interacting portions of
these managers into a struct, with `CFlatDB` interacting only with this
struct, while the manager inherits the struct and manages
load/store/update of the database through the `CFlatDB` instance
initialized within its scope, though the instance only has knowledge of
what is exposed through the limited parent struct.
## Additional information
* As regards to existing behaviour, `CFlatDB` is written entirely as a
header as it relies on templates to specialize itself for the object it
hooks into. Attempting to split the logic and function definitions into
separate files will require you to explicitly define template
specializations, which is tedious.
* `m_db` is defined as a pointer as you cannot instantiate a
forward-declared template (see [this Stack Overflow
answer](https://stackoverflow.com/a/12797282) for more information),
which is done when defined as a member in the object scope.
* The conditional cache flush predicating on RPC _not_ being in the
warm-up state has been replaced with unconditional flushing of the
database on object destruction (@UdjinM6, is this acceptable?)
## TODOs
This is a list of things that aren't within the scope of this pull
request but should be addressed in subsequent pull requests
* [ ] Definition of an interface that `CFlatDB` stores are expected to
implement
* [ ] Lock annotations for all potential uses of members protected by
the `cs` mutex in each manager object and store
* [ ] Additional comments documenting what each function and member does
* [ ] Deglobalization of affected managers
---------
Co-authored-by: Kittywhiskers Van Gogh <63189531+kittywhiskers@users.noreply.github.com>
2023-09-24 16:50:21 +02:00
if ( mmetaman - > GetMetaInfo ( connectToDmn - > proTxHash ) - > OutboundFailedTooManyTimes ( ) ) {
2022-07-14 20:38:02 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- failed to connect to masternode %s too many times \n " , __func__ , connectToDmn - > proTxHash . ToString ( ) ) ;
2022-07-07 09:49:34 +02:00
}
2020-03-17 10:04:31 +01:00
}
2017-01-21 20:03:55 +01:00
}
}
2012-07-26 02:48:39 +02:00
// if successful, this moves the passed grant to the constructed node
2022-06-19 08:02:28 +02:00
void CConnman : : OpenNetworkConnection ( const CAddress & addrConnect , bool fCountFailure , CSemaphoreGrant * grantOutbound , const char * pszDest , bool fOneShot , bool fFeeler , bool manual_connection , bool block_relay_only , MasternodeConn masternode_connection , MasternodeProbeConn masternode_probe_connection )
2010-08-29 18:58:15 +02:00
{
//
// Initiate outbound network connection
//
2017-08-09 18:06:31 +02:00
if ( interruptNet ) {
2018-02-02 09:50:12 +01:00
return ;
2017-08-09 18:06:31 +02:00
}
2017-09-11 15:38:14 +02:00
if ( ! fNetworkActive ) {
2018-02-02 09:50:12 +01:00
return ;
2018-02-21 17:32:08 +01:00
}
2022-07-07 09:49:55 +02:00
auto getIpStr = [ & ] ( ) {
if ( fLogIPs ) {
return addrConnect . ToString ( false ) ;
} else {
return std : : string ( " new peer " ) ;
}
} ;
2014-05-24 11:14:52 +02:00
if ( ! pszDest ) {
2022-06-10 11:16:05 +02:00
// banned, discouraged or exact match?
if ( ( m_banman & & ( m_banman - > IsDiscouraged ( addrConnect ) | | m_banman - > IsBanned ( addrConnect ) ) ) | | FindNode ( addrConnect . ToStringIPPort ( ) ) )
2018-02-02 09:50:12 +01:00
return ;
2018-07-07 23:19:33 +02:00
// local and not a connection to itself?
bool fAllowLocal = Params ( ) . AllowMultiplePorts ( ) & & addrConnect . GetPort ( ) ! = GetListenPort ( ) ;
if ( ! fAllowLocal & & IsLocal ( addrConnect ) )
2018-02-02 09:50:12 +01:00
return ;
2022-07-07 09:49:55 +02:00
// Search for IP:PORT match:
// - if multiple ports for the same IP are allowed,
// - for probe connections
// Search for IP-only match otherwise
bool searchIPPort = Params ( ) . AllowMultiplePorts ( ) | | masternode_probe_connection = = MasternodeProbeConn : : IsConnection ;
bool skip = searchIPPort ?
FindNode ( static_cast < CService > ( addrConnect ) ) :
FindNode ( static_cast < CNetAddr > ( addrConnect ) ) ;
if ( skip ) {
LogPrintf ( " CConnman::%s -- Failed to open new connection to %s, already connected \n " , __func__ , getIpStr ( ) ) ;
2018-02-02 09:50:12 +01:00
return ;
2022-07-07 09:49:55 +02:00
}
2015-05-31 15:44:22 +02:00
} else if ( FindNode ( std : : string ( pszDest ) ) )
2018-02-02 09:50:12 +01:00
return ;
2010-08-29 18:58:15 +02:00
2020-04-17 12:21:00 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- connecting to %s \n " , __func__ , getIpStr ( ) ) ;
2022-06-19 08:02:28 +02:00
CNode * pnode = ConnectNode ( addrConnect , pszDest , fCountFailure , manual_connection , block_relay_only ) ;
2013-03-07 04:31:26 +01:00
2020-04-07 06:51:50 +02:00
if ( ! pnode ) {
2020-04-17 12:21:00 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- ConnectNode failed for %s \n " , __func__ , getIpStr ( ) ) ;
2018-02-02 09:50:12 +01:00
return ;
2020-04-07 06:51:50 +02:00
}
2021-06-23 10:10:42 +02:00
{
LOCK ( pnode - > cs_hSocket ) ;
2021-07-19 12:39:22 +02:00
LogPrint ( BCLog : : NET_NETCONN , " CConnman::%s -- successfully connected to %s, sock=%d, peer=%d \n " , __func__ , getIpStr ( ) , pnode - > hSocket , pnode - > GetId ( ) ) ;
2021-06-23 10:10:42 +02:00
}
2012-05-10 18:44:07 +02:00
if ( grantOutbound )
grantOutbound - > MoveTo ( pnode - > grantOutbound ) ;
2012-04-24 02:15:00 +02:00
if ( fOneShot )
pnode - > fOneShot = true ;
2017-07-17 12:39:12 +02:00
if ( fFeeler )
pnode - > fFeeler = true ;
2017-10-14 00:25:16 +02:00
if ( manual_connection )
pnode - > m_manual_connection = true ;
2022-06-19 08:02:28 +02:00
if ( masternode_connection = = MasternodeConn : : IsConnection )
2021-01-14 20:59:18 +01:00
pnode - > m_masternode_connection = true ;
2022-06-19 08:02:28 +02:00
if ( masternode_probe_connection = = MasternodeProbeConn : : IsConnection )
2021-01-14 20:59:18 +01:00
pnode - > m_masternode_probe_connection = true ;
2010-08-29 18:58:15 +02:00
2020-04-07 07:00:41 +02:00
{
2021-06-23 10:10:42 +02:00
LOCK2 ( cs_vNodes , pnode - > cs_hSocket ) ;
2020-04-07 07:00:41 +02:00
mapSocketToNode . emplace ( pnode - > hSocket , pnode ) ;
}
2017-09-08 01:00:49 +02:00
m_msgproc - > InitializeNode ( pnode ) ;
2017-01-24 22:51:22 +01:00
{
LOCK ( cs_vNodes ) ;
vNodes . push_back ( pnode ) ;
2020-04-07 17:58:38 +02:00
RegisterEvents ( pnode ) ;
2020-04-08 23:17:19 +02:00
WakeSelect ( ) ;
2017-01-24 22:51:22 +01:00
}
2010-08-29 18:58:15 +02:00
}
2022-06-19 08:02:28 +02:00
void CConnman : : OpenMasternodeConnection ( const CAddress & addrConnect , MasternodeProbeConn probe ) {
OpenNetworkConnection ( addrConnect , false , nullptr , nullptr , false , false , false , false , MasternodeConn : : IsConnection , probe ) ;
2018-01-17 16:09:08 +01:00
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : ThreadMessageHandler ( )
2010-08-29 18:58:15 +02:00
{
2020-06-18 20:51:24 +02:00
int64_t nLastSendMessagesTimeMasternodes = 0 ;
2020-04-07 06:43:13 +02:00
2017-08-09 18:06:31 +02:00
while ( ! flagInterruptMsgProc )
2010-08-29 18:58:15 +02:00
{
2017-07-12 03:20:12 +02:00
std : : vector < CNode * > vNodesCopy = CopyNodeVector ( ) ;
2010-08-29 18:58:15 +02:00
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
bool fMoreWork = false ;
2020-06-18 20:51:24 +02:00
bool fSkipSendMessagesForMasternodes = true ;
if ( GetTimeMillis ( ) - nLastSendMessagesTimeMasternodes > = 100 ) {
fSkipSendMessagesForMasternodes = false ;
nLastSendMessagesTimeMasternodes = GetTimeMillis ( ) ;
2020-04-07 06:43:13 +02:00
}
2013-11-15 12:24:34 +01:00
2019-07-05 09:06:28 +02:00
for ( CNode * pnode : vNodesCopy )
2010-08-29 18:58:15 +02:00
{
2013-03-01 01:41:28 +01:00
if ( pnode - > fDisconnect )
continue ;
2010-08-29 18:58:15 +02:00
// Receive messages
2020-06-18 20:51:24 +02:00
bool fMoreNodeWork = m_msgproc - > ProcessMessages ( pnode , flagInterruptMsgProc ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
fMoreWork | = ( fMoreNodeWork & & ! pnode - > fPauseSend ) ;
2017-08-09 18:06:31 +02:00
if ( flagInterruptMsgProc )
return ;
2010-08-29 18:58:15 +02:00
// Send messages
2021-01-14 20:59:18 +01:00
if ( ! fSkipSendMessagesForMasternodes | | ! pnode - > m_masternode_connection ) {
2017-01-19 20:19:29 +01:00
LOCK ( pnode - > cs_sendProcessing ) ;
2018-07-09 16:40:37 +02:00
m_msgproc - > SendMessages ( pnode ) ;
2012-04-06 18:39:12 +02:00
}
2017-09-08 01:00:49 +02:00
2017-08-09 18:06:31 +02:00
if ( flagInterruptMsgProc )
return ;
2010-08-29 18:58:15 +02:00
}
2017-03-05 20:16:12 +01:00
ReleaseNodeVector ( vNodesCopy ) ;
2013-11-15 12:24:34 +01:00
2017-11-08 21:28:35 +01:00
WAIT_LOCK ( mutexMsgProc , lock ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
if ( ! fMoreWork ) {
2021-06-09 14:06:31 +02:00
condMsgProc . wait_until ( lock , std : : chrono : : steady_clock : : now ( ) + std : : chrono : : milliseconds ( 100 ) , [ this ] ( ) EXCLUSIVE_LOCKS_REQUIRED ( mutexMsgProc ) { return fMsgProcWake ; } ) ;
2017-08-09 18:06:31 +02:00
}
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
fMsgProcWake = false ;
2010-08-29 18:58:15 +02:00
}
}
2020-11-18 17:13:27 +01:00
void CConnman : : ThreadI2PAcceptIncoming ( )
{
static constexpr auto err_wait_begin = 1 s ;
static constexpr auto err_wait_cap = 5 min ;
auto err_wait = err_wait_begin ;
2010-08-29 18:58:15 +02:00
2020-11-18 17:13:27 +01:00
bool advertising_listen_addr = false ;
i2p : : Connection conn ;
2010-08-29 18:58:15 +02:00
2020-11-18 17:13:27 +01:00
while ( ! interruptNet ) {
2010-08-29 18:58:15 +02:00
2020-11-18 17:13:27 +01:00
if ( ! m_i2p_sam_session - > Listen ( conn ) ) {
if ( advertising_listen_addr & & conn . me . IsValid ( ) ) {
RemoveLocal ( conn . me ) ;
advertising_listen_addr = false ;
}
interruptNet . sleep_for ( err_wait ) ;
if ( err_wait < err_wait_cap ) {
err_wait * = 2 ;
}
2010-08-29 18:58:15 +02:00
2020-11-18 17:13:27 +01:00
continue ;
}
if ( ! advertising_listen_addr ) {
2021-05-13 15:36:39 +02:00
AddLocal ( conn . me , LOCAL_MANUAL ) ;
2020-11-18 17:13:27 +01:00
advertising_listen_addr = true ;
}
if ( ! m_i2p_sam_session - > Accept ( conn ) ) {
continue ;
}
2023-07-14 17:11:45 +02:00
CreateNodeFromAcceptedSocket ( conn . sock - > Release ( ) , NetPermissionFlags : : PF_NONE ,
2020-11-18 17:13:27 +01:00
CAddress { conn . me , NODE_NONE } , CAddress { conn . peer , NODE_NONE } ) ;
}
}
2010-08-29 18:58:15 +02:00
2020-05-08 18:17:47 +02:00
bool CConnman : : BindListenPort ( const CService & addrBind , bilingual_str & strError , NetPermissionFlags permissions )
2010-08-29 18:58:15 +02:00
{
int nOne = 1 ;
// Create socket for listening for incoming connections
2012-05-11 15:28:59 +02:00
struct sockaddr_storage sockaddr ;
socklen_t len = sizeof ( sockaddr ) ;
if ( ! addrBind . GetSockAddr ( ( struct sockaddr * ) & sockaddr , & len ) )
{
2020-05-08 18:17:47 +02:00
strError = strprintf ( Untranslated ( " Error: Bind address family for %s not supported " ) , addrBind . ToString ( ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2012-05-11 15:28:59 +02:00
return false ;
}
2022-10-26 13:25:11 +02:00
std : : unique_ptr < Sock > sock = CreateSock ( addrBind ) ;
if ( ! sock ) {
2020-05-08 18:17:47 +02:00
strError = strprintf ( Untranslated ( " Error: Couldn't open socket for incoming connections (socket returned error %s) " ) , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2010-08-29 18:58:15 +02:00
return false ;
}
// Allow binding if the port is still in TIME_WAIT state after
2015-08-20 21:50:13 +02:00
// the program was closed and restarted.
2022-10-26 13:25:11 +02:00
setsockopt ( sock - > Get ( ) , SOL_SOCKET , SO_REUSEADDR , ( sockopt_arg_type ) & nOne , sizeof ( int ) ) ;
2010-08-29 18:58:15 +02:00
2012-05-11 15:28:59 +02:00
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if ( addrBind . IsIPv6 ( ) ) {
# ifdef IPV6_V6ONLY
2022-10-26 13:25:11 +02:00
setsockopt ( sock - > Get ( ) , IPPROTO_IPV6 , IPV6_V6ONLY , ( sockopt_arg_type ) & nOne , sizeof ( int ) ) ;
2013-07-13 13:05:04 +02:00
# endif
2012-05-11 15:28:59 +02:00
# ifdef WIN32
2014-06-24 09:03:18 +02:00
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED ;
2022-10-26 13:25:11 +02:00
setsockopt ( sock - > Get ( ) , IPPROTO_IPV6 , IPV6_PROTECTION_LEVEL , ( const char * ) & nProtLevel , sizeof ( int ) ) ;
2012-05-11 15:28:59 +02:00
# endif
}
2022-10-26 13:25:11 +02:00
if ( : : bind ( sock - > Get ( ) , ( struct sockaddr * ) & sockaddr , len ) = = SOCKET_ERROR )
2010-08-29 18:58:15 +02:00
{
int nErr = WSAGetLastError ( ) ;
if ( nErr = = WSAEADDRINUSE )
2020-05-08 18:17:47 +02:00
strError = strprintf ( _ ( " Unable to bind to %s on this computer. %s is probably already running. " ) , addrBind . ToString ( ) , PACKAGE_NAME ) ;
2010-08-29 18:58:15 +02:00
else
2020-05-08 18:17:47 +02:00
strError = strprintf ( _ ( " Unable to bind to %s on this computer (bind returned error %s) " ) , addrBind . ToString ( ) , NetworkErrorString ( nErr ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2010-08-29 18:58:15 +02:00
return false ;
}
2014-01-16 16:15:27 +01:00
LogPrintf ( " Bound to %s \n " , addrBind . ToString ( ) ) ;
2010-08-29 18:58:15 +02:00
// Listen for incoming connections
2022-10-26 13:25:11 +02:00
if ( listen ( sock - > Get ( ) , SOMAXCONN ) = = SOCKET_ERROR )
2010-08-29 18:58:15 +02:00
{
2020-05-08 18:17:47 +02:00
strError = strprintf ( _ ( " Error: Listening for incoming connections failed (listen returned error %s) " ) , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2010-08-29 18:58:15 +02:00
return false ;
}
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode = = SOCKETEVENTS_KQUEUE ) {
struct kevent event ;
2022-10-26 13:25:11 +02:00
EV_SET ( & event , sock - > Get ( ) , EVFILT_READ , EV_ADD , 0 , 0 , nullptr ) ;
2020-12-30 20:34:42 +01:00
if ( kevent ( kqueuefd , & event , 1 , nullptr , 0 , nullptr ) ! = 0 ) {
2020-05-08 18:17:47 +02:00
strError = strprintf ( _ ( " Error: failed to add socket to kqueuefd (kevent returned error %s) " ) , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2020-12-30 20:34:42 +01:00
return false ;
}
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode = = SOCKETEVENTS_EPOLL ) {
epoll_event event ;
2022-10-26 13:25:11 +02:00
event . data . fd = sock - > Get ( ) ;
2020-04-07 17:58:38 +02:00
event . events = EPOLLIN ;
2022-10-26 13:25:11 +02:00
if ( epoll_ctl ( epollfd , EPOLL_CTL_ADD , sock - > Get ( ) , & event ) ! = 0 ) {
2020-05-08 18:17:47 +02:00
strError = strprintf ( _ ( " Error: failed to add socket to epollfd (epoll_ctl returned error %s) " ) , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
LogPrintf ( " %s \n " , strError . original ) ;
2020-04-07 17:58:38 +02:00
return false ;
}
}
# endif
2022-10-26 13:25:11 +02:00
vhListenSocket . push_back ( ListenSocket ( sock - > Release ( ) , permissions ) ) ;
2012-05-11 15:28:59 +02:00
2010-08-29 18:58:15 +02:00
return true ;
}
2020-06-13 20:21:30 +02:00
void Discover ( )
2010-08-29 18:58:15 +02:00
{
2012-05-24 19:02:21 +02:00
if ( ! fDiscover )
2012-02-19 20:44:35 +01:00
return ;
2010-08-29 18:58:15 +02:00
2011-10-07 17:02:21 +02:00
# ifdef WIN32
2012-07-26 02:48:39 +02:00
// Get local host IP
2014-11-13 15:23:15 +01:00
char pszHostName [ 256 ] = " " ;
2010-08-29 18:58:15 +02:00
if ( gethostname ( pszHostName , sizeof ( pszHostName ) ) ! = SOCKET_ERROR )
{
2017-07-12 03:20:12 +02:00
std : : vector < CNetAddr > vaddr ;
2017-09-02 22:07:11 +02:00
if ( LookupHost ( pszHostName , vaddr , 0 , true ) )
2012-05-01 01:44:59 +02:00
{
2019-07-05 09:06:28 +02:00
for ( const CNetAddr & addr : vaddr )
2012-05-01 01:44:59 +02:00
{
2014-11-13 15:20:57 +01:00
if ( AddLocal ( addr , LOCAL_IF ) )
LogPrintf ( " %s: %s - %s \n " , __func__ , pszHostName , addr . ToString ( ) ) ;
2012-05-01 01:44:59 +02:00
}
}
2010-08-29 18:58:15 +02:00
}
2018-09-10 18:07:53 +02:00
# elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS)
2010-08-29 18:58:15 +02:00
// Get local host ip
struct ifaddrs * myaddrs ;
if ( getifaddrs ( & myaddrs ) = = 0 )
{
2019-08-06 05:08:33 +02:00
for ( struct ifaddrs * ifa = myaddrs ; ifa ! = nullptr ; ifa = ifa - > ifa_next )
2010-08-29 18:58:15 +02:00
{
2019-08-06 05:08:33 +02:00
if ( ifa - > ifa_addr = = nullptr ) continue ;
2010-08-29 18:58:15 +02:00
if ( ( ifa - > ifa_flags & IFF_UP ) = = 0 ) continue ;
if ( strcmp ( ifa - > ifa_name , " lo " ) = = 0 ) continue ;
if ( strcmp ( ifa - > ifa_name , " lo0 " ) = = 0 ) continue ;
if ( ifa - > ifa_addr - > sa_family = = AF_INET )
{
struct sockaddr_in * s4 = ( struct sockaddr_in * ) ( ifa - > ifa_addr ) ;
2012-02-12 13:45:24 +01:00
CNetAddr addr ( s4 - > sin_addr ) ;
2012-03-31 17:58:25 +02:00
if ( AddLocal ( addr , LOCAL_IF ) )
2014-11-13 15:20:57 +01:00
LogPrintf ( " %s: IPv4 %s: %s \n " , __func__ , ifa - > ifa_name , addr . ToString ( ) ) ;
2010-08-29 18:58:15 +02:00
}
else if ( ifa - > ifa_addr - > sa_family = = AF_INET6 )
{
struct sockaddr_in6 * s6 = ( struct sockaddr_in6 * ) ( ifa - > ifa_addr ) ;
2012-02-12 13:45:24 +01:00
CNetAddr addr ( s6 - > sin6_addr ) ;
2012-03-31 17:58:25 +02:00
if ( AddLocal ( addr , LOCAL_IF ) )
2014-11-13 15:20:57 +01:00
LogPrintf ( " %s: IPv6 %s: %s \n " , __func__ , ifa - > ifa_name , addr . ToString ( ) ) ;
2010-08-29 18:58:15 +02:00
}
}
freeifaddrs ( myaddrs ) ;
}
# endif
2012-02-19 20:44:35 +01:00
}
2017-09-11 15:38:14 +02:00
void CConnman : : SetNetworkActive ( bool active )
{
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " SetNetworkActive: %s \n " , active ) ;
2017-09-11 15:38:14 +02:00
2017-07-26 11:11:25 +02:00
if ( fNetworkActive = = active ) {
return ;
}
fNetworkActive = active ;
2017-09-11 15:38:14 +02:00
2020-09-11 14:07:34 +02:00
// Always call the Reset() if the network gets enabled/disabled to make sure the sync process
// gets a reset if its outdated..
refactor: begin to de-globalize masternodeSync (#5103)
<!--
*** Please remove the following help text before submitting: ***
Provide a general summary of your changes in the Title above
Pull requests without a rationale and clear improvement may be closed
immediately.
Please provide clear motivation for your patch and explain how it
improves
Dash Core user experience or Dash Core developer experience
significantly:
* Any test improvements or new tests that improve coverage are always
welcome.
* All other changes should have accompanying unit tests (see
`src/test/`) or
functional tests (see `test/`). Contributors should note which tests
cover
modified code. If no tests exist for a region of modified code, new
tests
should accompany the change.
* Bug fixes are most welcome when they come with steps to reproduce or
an
explanation of the potential issue as well as reasoning for the way the
bug
was fixed.
* Features are welcome, but might be rejected due to design or scope
issues.
If a feature is based on a lot of dependencies, contributors should
first
consider building the system outside of Dash Core, if possible.
-->
## Issue being fixed or feature implemented
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
minimizing global uses
## What was done?
<!--- Describe your changes in detail -->
Started the deglobalization, a future PR should be done to continue this
deglobalization
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
## Breaking Changes
<!--- Please describe any breaking changes your code introduces -->
none
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have added or updated relevant unit/integration/functional/e2e
tests
- [x] I have made corresponding changes to the documentation
**For repository code-owners and collaborators only**
- [x] I have assigned this pull request to a milestone
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
2023-01-04 21:37:20 +01:00
: : masternodeSync - > Reset ( ) ;
2020-09-11 14:07:34 +02:00
2017-09-11 15:38:14 +02:00
uiInterface . NotifyNetworkActiveChanged ( fNetworkActive ) ;
}
2023-02-16 07:34:06 +01:00
CConnman : : CConnman ( uint64_t nSeed0In , uint64_t nSeed1In , CAddrMan & addrman_in ) :
addrman ( addrman_in ) , nSeed0 ( nSeed0In ) , nSeed1 ( nSeed1In )
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
2017-07-21 11:35:19 +02:00
{
2017-11-02 20:13:17 +01:00
SetTryNewOutboundPeer ( false ) ;
Merge #10977: [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&)
11dd29b [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest& request) (practicalswift)
Pull request description:
When running `test_bitcoin` under Valgrind I found the following issue:
```
$ valgrind src/test/test_bitcoin
...
==10465== Use of uninitialised value of size 8
==10465== at 0x6D09B61: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B1BB: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<unsigned long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B36C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D17699: std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x4CAAD7: operator<< (ostream:171)
==10465== by 0x4CAAD7: formatValue<ServiceFlags> (tinyformat.h:345)
==10465== by 0x4CAAD7: void tinyformat::detail::FormatArg::formatImpl<ServiceFlags>(std::ostream&, char const*, char const*, int, void const*) (tinyformat.h:523)
==10465== by 0x1924D4: format (tinyformat.h:510)
==10465== by 0x1924D4: tinyformat::detail::formatImpl(std::ostream&, char const*, tinyformat::detail::FormatArg const*, int) (tinyformat.h:803)
==10465== by 0x553A55: vformat (tinyformat.h:947)
==10465== by 0x553A55: format<ServiceFlags> (tinyformat.h:957)
==10465== by 0x553A55: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > tinyformat::format<ServiceFlags>(char const*, ServiceFlags const&) (tinyformat.h:966)
==10465== by 0x54C952: getnetworkinfo(JSONRPCRequest const&) (net.cpp:462)
==10465== by 0x28EDB5: CallRPC(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (rpc_tests.cpp:31)
==10465== by 0x293947: rpc_tests::rpc_togglenetwork::test_method() (rpc_tests.cpp:88)
==10465== by 0x2950E5: rpc_tests::rpc_togglenetwork_invoker() (rpc_tests.cpp:84)
==10465== by 0x182496: invoke<void (*)()> (callback.hpp:56)
==10465== by 0x182496: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
...
```
The read of the uninitialized variable `nLocalServices` is triggered by `g_connman->GetLocalServices()` in `getnetworkinfo(const JSONRPCRequest& request)` (`net.cpp:462`):
```c++
UniValue getnetworkinfo(const JSONRPCRequest& request)
{
...
if(g_connman)
obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
...
}
```
The reason for the uninitialized `nLocalServices` is that `CConnman::Start(...)` is not called
by the tests, and hence the initialization normally performed by `CConnman::Start(...)` is
not done.
This commit adds a method `Init(const Options& connOptions)` which is called by both the
constructor and `CConnman::Start(...)`. This method initializes `nLocalServices` and the other
relevant values from the supplied `Options` object.
Tree-SHA512: d8742363acffd03b2ee081cc56840275569e17edc6fa4bb1dee4a5971ffe4b8ab1d2fe7b68f98a086bf133b7ec46f4e471243ca08b45bf82356e8c831a5a5f21
2017-08-05 13:23:10 +02:00
Options connOptions ;
Init ( connOptions ) ;
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
2017-07-21 11:35:19 +02:00
}
NodeId CConnman : : GetNewNodeId ( )
2012-02-19 20:44:35 +01:00
{
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
2017-07-21 11:35:19 +02:00
return nLastNodeId . fetch_add ( 1 , std : : memory_order_relaxed ) ;
}
2017-06-26 14:42:46 +02:00
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
bool CConnman : : Bind ( const CService & addr , unsigned int flags , NetPermissionFlags permissions ) {
2021-05-11 12:51:41 +02:00
if ( ! ( flags & BF_EXPLICIT ) & & ! IsReachable ( addr ) ) {
2017-06-26 14:42:46 +02:00
return false ;
2021-05-11 12:51:41 +02:00
}
2020-05-08 18:17:47 +02:00
bilingual_str strError ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
if ( ! BindListenPort ( addr , strError , permissions ) ) {
2017-06-26 14:42:46 +02:00
if ( ( flags & BF_REPORT_ERROR ) & & clientInterface ) {
clientInterface - > ThreadSafeMessageBox ( strError , " " , CClientUIInterface : : MSG_ERROR ) ;
}
return false ;
}
2023-04-17 10:27:07 +02:00
2021-05-11 12:51:41 +02:00
if ( addr . IsRoutable ( ) & & fDiscover & & ! ( flags & BF_DONT_ADVERTISE ) & & ! NetPermissions : : HasFlag ( permissions , NetPermissionFlags : : PF_NOBAN ) ) {
2023-04-17 10:27:07 +02:00
AddLocal ( addr , LOCAL_BIND ) ;
}
2017-06-26 14:42:46 +02:00
return true ;
}
2023-04-17 10:27:07 +02:00
bool CConnman : : InitBinds (
const std : : vector < CService > & binds ,
const std : : vector < NetWhitebindPermissions > & whiteBinds ,
const std : : vector < CService > & onion_binds )
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
{
2017-06-26 14:42:46 +02:00
bool fBound = false ;
for ( const auto & addrBind : binds ) {
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
fBound | = Bind ( addrBind , ( BF_EXPLICIT | BF_REPORT_ERROR ) , NetPermissionFlags : : PF_NONE ) ;
2017-06-26 14:42:46 +02:00
}
for ( const auto & addrBind : whiteBinds ) {
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
fBound | = Bind ( addrBind . m_service , ( BF_EXPLICIT | BF_REPORT_ERROR ) , addrBind . m_flags ) ;
2017-06-26 14:42:46 +02:00
}
if ( binds . empty ( ) & & whiteBinds . empty ( ) ) {
struct in_addr inaddr_any ;
2023-04-17 10:27:07 +02:00
inaddr_any . s_addr = htonl ( INADDR_ANY ) ;
2018-07-18 15:02:56 +02:00
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT ;
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1973afe071a07c63ba1038eefe13f0f Add functional tests for flexible whitebind/list (nicolas.dorier)
d541fa391844f658bd7035659b5b16695733dd56 Replace the use of fWhitelisted by permission checks (nicolas.dorier)
ecd5cf7ea4c3644a30092100ffc399e30e193275 Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)
e5b26deaaa6842f7dd7c4537ede000f965ea0189 Make whitebind/whitelist permissions more flexible (nicolas.dorier)
Pull request description:
# Motivation
In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`.
Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum.
It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes.
When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute.
Doing so will also make follow up idea very easy to implement in a backward compatible way.
# Implementation details
The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`.
The following permissions exists:
* ForceRelay
* Relay
* NoBan
* BloomFilter
* Mempool
Example:
* `-whitelist=bloomfilter@127.0.0.1/32`.
* `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`.
If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible)
When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`.
To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node.
`-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`.
# Follow up idea
Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way:
* Changing `connect` at rpc and config file level to understand the permissions flags.
* Changing the permissions of a peer at RPC level.
ACKs for top commit:
laanwj:
re-ACK c5b404e8f1973afe071a07c63ba1038eefe13f0f
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
2019-08-14 16:35:54 +02:00
fBound | = Bind ( CService ( inaddr6_any , GetListenPort ( ) ) , BF_NONE , NetPermissionFlags : : PF_NONE ) ;
fBound | = Bind ( CService ( inaddr_any , GetListenPort ( ) ) , ! fBound ? BF_REPORT_ERROR : BF_NONE , NetPermissionFlags : : PF_NONE ) ;
2017-06-26 14:42:46 +02:00
}
2023-04-17 10:27:07 +02:00
for ( const auto & addr_bind : onion_binds ) {
fBound | = Bind ( addr_bind , BF_EXPLICIT | BF_DONT_ADVERTISE , NetPermissionFlags : : PF_NONE ) ;
}
2017-06-26 14:42:46 +02:00
return fBound ;
}
Merge #10977: [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&)
11dd29b [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest& request) (practicalswift)
Pull request description:
When running `test_bitcoin` under Valgrind I found the following issue:
```
$ valgrind src/test/test_bitcoin
...
==10465== Use of uninitialised value of size 8
==10465== at 0x6D09B61: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B1BB: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<unsigned long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B36C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D17699: std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x4CAAD7: operator<< (ostream:171)
==10465== by 0x4CAAD7: formatValue<ServiceFlags> (tinyformat.h:345)
==10465== by 0x4CAAD7: void tinyformat::detail::FormatArg::formatImpl<ServiceFlags>(std::ostream&, char const*, char const*, int, void const*) (tinyformat.h:523)
==10465== by 0x1924D4: format (tinyformat.h:510)
==10465== by 0x1924D4: tinyformat::detail::formatImpl(std::ostream&, char const*, tinyformat::detail::FormatArg const*, int) (tinyformat.h:803)
==10465== by 0x553A55: vformat (tinyformat.h:947)
==10465== by 0x553A55: format<ServiceFlags> (tinyformat.h:957)
==10465== by 0x553A55: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > tinyformat::format<ServiceFlags>(char const*, ServiceFlags const&) (tinyformat.h:966)
==10465== by 0x54C952: getnetworkinfo(JSONRPCRequest const&) (net.cpp:462)
==10465== by 0x28EDB5: CallRPC(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (rpc_tests.cpp:31)
==10465== by 0x293947: rpc_tests::rpc_togglenetwork::test_method() (rpc_tests.cpp:88)
==10465== by 0x2950E5: rpc_tests::rpc_togglenetwork_invoker() (rpc_tests.cpp:84)
==10465== by 0x182496: invoke<void (*)()> (callback.hpp:56)
==10465== by 0x182496: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
...
```
The read of the uninitialized variable `nLocalServices` is triggered by `g_connman->GetLocalServices()` in `getnetworkinfo(const JSONRPCRequest& request)` (`net.cpp:462`):
```c++
UniValue getnetworkinfo(const JSONRPCRequest& request)
{
...
if(g_connman)
obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
...
}
```
The reason for the uninitialized `nLocalServices` is that `CConnman::Start(...)` is not called
by the tests, and hence the initialization normally performed by `CConnman::Start(...)` is
not done.
This commit adds a method `Init(const Options& connOptions)` which is called by both the
constructor and `CConnman::Start(...)`. This method initializes `nLocalServices` and the other
relevant values from the supplied `Options` object.
Tree-SHA512: d8742363acffd03b2ee081cc56840275569e17edc6fa4bb1dee4a5971ffe4b8ab1d2fe7b68f98a086bf133b7ec46f4e471243ca08b45bf82356e8c831a5a5f21
2017-08-05 13:23:10 +02:00
bool CConnman : : Start ( CScheduler & scheduler , const Options & connOptions )
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
2017-07-21 11:35:19 +02:00
{
Merge #10977: [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&)
11dd29b [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest& request) (practicalswift)
Pull request description:
When running `test_bitcoin` under Valgrind I found the following issue:
```
$ valgrind src/test/test_bitcoin
...
==10465== Use of uninitialised value of size 8
==10465== at 0x6D09B61: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B1BB: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<unsigned long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D0B36C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, unsigned long) const (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x6D17699: std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==10465== by 0x4CAAD7: operator<< (ostream:171)
==10465== by 0x4CAAD7: formatValue<ServiceFlags> (tinyformat.h:345)
==10465== by 0x4CAAD7: void tinyformat::detail::FormatArg::formatImpl<ServiceFlags>(std::ostream&, char const*, char const*, int, void const*) (tinyformat.h:523)
==10465== by 0x1924D4: format (tinyformat.h:510)
==10465== by 0x1924D4: tinyformat::detail::formatImpl(std::ostream&, char const*, tinyformat::detail::FormatArg const*, int) (tinyformat.h:803)
==10465== by 0x553A55: vformat (tinyformat.h:947)
==10465== by 0x553A55: format<ServiceFlags> (tinyformat.h:957)
==10465== by 0x553A55: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > tinyformat::format<ServiceFlags>(char const*, ServiceFlags const&) (tinyformat.h:966)
==10465== by 0x54C952: getnetworkinfo(JSONRPCRequest const&) (net.cpp:462)
==10465== by 0x28EDB5: CallRPC(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) (rpc_tests.cpp:31)
==10465== by 0x293947: rpc_tests::rpc_togglenetwork::test_method() (rpc_tests.cpp:88)
==10465== by 0x2950E5: rpc_tests::rpc_togglenetwork_invoker() (rpc_tests.cpp:84)
==10465== by 0x182496: invoke<void (*)()> (callback.hpp:56)
==10465== by 0x182496: boost::unit_test::ut_detail::callback0_impl_t<boost::unit_test::ut_detail::unused, void (*)()>::invoke() (callback.hpp:89)
...
```
The read of the uninitialized variable `nLocalServices` is triggered by `g_connman->GetLocalServices()` in `getnetworkinfo(const JSONRPCRequest& request)` (`net.cpp:462`):
```c++
UniValue getnetworkinfo(const JSONRPCRequest& request)
{
...
if(g_connman)
obj.push_back(Pair("localservices", strprintf("%016x", g_connman->GetLocalServices())));
...
}
```
The reason for the uninitialized `nLocalServices` is that `CConnman::Start(...)` is not called
by the tests, and hence the initialization normally performed by `CConnman::Start(...)` is
not done.
This commit adds a method `Init(const Options& connOptions)` which is called by both the
constructor and `CConnman::Start(...)`. This method initializes `nLocalServices` and the other
relevant values from the supplied `Options` object.
Tree-SHA512: d8742363acffd03b2ee081cc56840275569e17edc6fa4bb1dee4a5971ffe4b8ab1d2fe7b68f98a086bf133b7ec46f4e471243ca08b45bf82356e8c831a5a5f21
2017-08-05 13:23:10 +02:00
Init ( connOptions ) ;
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode = = SOCKETEVENTS_KQUEUE ) {
kqueuefd = kqueue ( ) ;
if ( kqueuefd = = - 1 ) {
LogPrintf ( " kqueue failed \n " ) ;
return false ;
}
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode = = SOCKETEVENTS_EPOLL ) {
epollfd = epoll_create1 ( 0 ) ;
if ( epollfd = = - 1 ) {
LogPrintf ( " epoll_create1 failed \n " ) ;
return false ;
}
}
# endif
2023-04-17 10:27:07 +02:00
if ( fListen & & ! InitBinds ( connOptions . vBinds , connOptions . vWhiteBinds , connOptions . onion_binds ) ) {
2017-06-26 14:42:46 +02:00
if ( clientInterface ) {
clientInterface - > ThreadSafeMessageBox (
2020-05-08 18:17:47 +02:00
_ ( " Failed to listen on any port. Use -listen=0 if you want this. " ) ,
2017-06-26 14:42:46 +02:00
" " , CClientUIInterface : : MSG_ERROR ) ;
}
return false ;
}
2020-11-18 17:13:27 +01:00
proxyType i2p_sam ;
if ( GetProxy ( NET_I2P , i2p_sam ) ) {
m_i2p_sam_session = std : : make_unique < i2p : : sam : : Session > ( GetDataDir ( ) / " i2p_private_key " ,
i2p_sam . proxy , & interruptNet ) ;
}
2017-05-31 17:29:29 +02:00
for ( const auto & strDest : connOptions . vSeedNodes ) {
AddOneShot ( strDest ) ;
}
2017-03-01 11:16:11 +01:00
if ( clientInterface ) {
2022-03-24 05:13:51 +01:00
clientInterface - > InitMessage ( _ ( " Loading P2P addresses... " ) . translated ) ;
2017-03-01 11:16:11 +01:00
}
2017-07-04 23:39:05 +02:00
// Load addresses from peers.dat
2014-09-18 14:08:43 +02:00
int64_t nStart = GetTimeMillis ( ) ;
{
CAddrDB adb ;
2017-07-04 23:39:05 +02:00
if ( adb . Read ( addrman ) )
LogPrintf ( " Loaded %i addresses from peers.dat %dms \n " , addrman . size ( ) , GetTimeMillis ( ) - nStart ) ;
2017-07-05 01:26:13 +02:00
else {
2017-07-05 02:40:22 +02:00
addrman . Clear ( ) ; // Addrman can be in an inconsistent state after failure, reset it
2021-02-23 16:03:05 +01:00
LogPrintf ( " Recreating peers.dat \n " ) ;
2017-07-05 01:26:13 +02:00
DumpAddresses ( ) ;
}
2014-09-18 14:08:43 +02:00
}
2015-06-19 15:27:37 +02:00
2023-09-10 19:02:53 +02:00
if ( m_use_addrman_outgoing ) {
// Load addresses from anchors.dat
m_anchors = ReadAnchors ( GetDataDir ( ) / ANCHORS_DATABASE_FILENAME ) ;
if ( m_anchors . size ( ) > MAX_BLOCK_RELAY_ONLY_ANCHORS ) {
m_anchors . resize ( MAX_BLOCK_RELAY_ONLY_ANCHORS ) ;
}
LogPrintf ( " %i block-relay-only anchors will be tried for connections. \n " , m_anchors . size ( ) ) ;
}
2022-03-24 05:13:51 +01:00
uiInterface . InitMessage ( _ ( " Starting network threads... " ) . translated ) ;
2016-08-04 12:21:59 +02:00
2014-09-18 14:08:43 +02:00
fAddressesInitialized = true ;
2019-08-06 05:08:33 +02:00
if ( semOutbound = = nullptr ) {
2012-05-10 18:44:07 +02:00
// initialize semaphore
2022-10-15 20:35:50 +02:00
semOutbound = std : : make_unique < CSemaphore > ( std : : min ( m_max_outbound , nMaxConnections ) ) ;
2012-05-10 18:44:07 +02:00
}
2019-08-06 05:08:33 +02:00
if ( semAddnode = = nullptr ) {
2017-01-06 16:47:05 +01:00
// initialize semaphore
2022-10-15 20:35:50 +02:00
semAddnode = std : : make_unique < CSemaphore > ( nMaxAddnode ) ;
2017-01-06 16:47:05 +01:00
}
2012-05-10 18:44:07 +02:00
2010-08-29 18:58:15 +02:00
//
// Start threads
//
2017-09-08 01:00:49 +02:00
assert ( m_msgproc ) ;
2017-08-09 18:06:31 +02:00
InterruptSocks5 ( false ) ;
interruptNet . reset ( ) ;
flagInterruptMsgProc = false ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
{
2017-11-08 21:28:35 +01:00
LOCK ( mutexMsgProc ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
fMsgProcWake = false ;
}
2020-04-06 07:18:50 +02:00
# ifdef USE_WAKEUP_PIPE
2019-02-15 16:30:42 +01:00
if ( pipe ( wakeupPipe ) ! = 0 ) {
2019-02-14 18:50:18 +01:00
wakeupPipe [ 0 ] = wakeupPipe [ 1 ] = - 1 ;
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " pipe() for wakeupPipe failed \n " ) ;
2019-02-15 16:30:42 +01:00
} else {
int fFlags = fcntl ( wakeupPipe [ 0 ] , F_GETFL , 0 ) ;
if ( fcntl ( wakeupPipe [ 0 ] , F_SETFL , fFlags | O_NONBLOCK ) = = - 1 ) {
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " fcntl for O_NONBLOCK on wakeupPipe failed \n " ) ;
2019-02-15 16:30:42 +01:00
}
fFlags = fcntl ( wakeupPipe [ 1 ] , F_GETFL , 0 ) ;
if ( fcntl ( wakeupPipe [ 1 ] , F_SETFL , fFlags | O_NONBLOCK ) = = - 1 ) {
2019-05-22 23:51:39 +02:00
LogPrint ( BCLog : : NET , " fcntl for O_NONBLOCK on wakeupPipe failed \n " ) ;
2019-02-15 16:30:42 +01:00
}
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode = = SOCKETEVENTS_KQUEUE ) {
struct kevent event ;
EV_SET ( & event , wakeupPipe [ 0 ] , EVFILT_READ , EV_ADD , 0 , 0 , nullptr ) ;
int r = kevent ( kqueuefd , & event , 1 , nullptr , 0 , nullptr ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- kevent(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
kqueuefd , EV_ADD , wakeupPipe [ 0 ] , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
return false ;
}
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode = = SOCKETEVENTS_EPOLL ) {
epoll_event event ;
event . events = EPOLLIN ;
event . data . fd = wakeupPipe [ 0 ] ;
int r = epoll_ctl ( epollfd , EPOLL_CTL_ADD , wakeupPipe [ 0 ] , & event ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- epoll_ctl(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
2020-04-23 11:33:36 +02:00
epollfd , EPOLL_CTL_ADD , wakeupPipe [ 0 ] , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
2020-04-07 17:58:38 +02:00
return false ;
}
}
# endif
2019-02-14 18:50:18 +01:00
}
# endif
2017-08-09 18:06:31 +02:00
// Send and receive from sockets, accept connections
2023-08-26 11:50:37 +02:00
threadSocketHandler = std : : thread ( & util : : TraceThread , " net " , [ this ] { ThreadSocketHandler ( ) ; } ) ;
2010-08-29 18:58:15 +02:00
2019-06-24 18:44:27 +02:00
if ( ! gArgs . GetBoolArg ( " -dnsseed " , true ) )
2013-09-18 12:38:08 +02:00
LogPrintf ( " DNS seeding disabled \n " ) ;
2011-11-21 18:25:00 +01:00
else
2023-08-26 11:50:37 +02:00
threadDNSAddressSeed = std : : thread ( & util : : TraceThread , " dnsseed " , [ this ] { ThreadDNSAddressSeed ( ) ; } ) ;
2010-08-29 18:58:15 +02:00
2011-12-17 01:48:03 +01:00
// Initiate outbound connections from -addnode
2023-08-26 11:50:37 +02:00
threadOpenAddedConnections = std : : thread ( & util : : TraceThread , " addcon " , [ this ] { ThreadOpenAddedConnections ( ) ; } ) ;
2011-12-17 01:48:03 +01:00
2017-09-06 01:54:31 +02:00
if ( connOptions . m_use_addrman_outgoing & & ! connOptions . m_specified_outgoing . empty ( ) ) {
if ( clientInterface ) {
clientInterface - > ThreadSafeMessageBox (
2020-05-08 18:17:47 +02:00
_ ( " Cannot provide specific connections and have addrman find outgoing connections at the same. " ) ,
2017-09-06 01:54:31 +02:00
" " , CClientUIInterface : : MSG_ERROR ) ;
}
return false ;
}
2023-08-26 11:50:37 +02:00
if ( connOptions . m_use_addrman_outgoing | | ! connOptions . m_specified_outgoing . empty ( ) ) {
threadOpenConnections = std : : thread (
& util : : TraceThread , " opencon " ,
[ this , connect = connOptions . m_specified_outgoing ] { ThreadOpenConnections ( connect ) ; } ) ;
}
2010-08-29 18:58:15 +02:00
2017-01-21 20:03:55 +01:00
// Initiate masternode connections
2023-08-26 11:50:37 +02:00
threadOpenMasternodeConnections = std : : thread ( & util : : TraceThread , " mncon " , [ this ] { ThreadOpenMasternodeConnections ( ) ; } ) ;
2017-01-21 20:03:55 +01:00
2010-08-29 18:58:15 +02:00
// Process messages
2023-08-26 11:50:37 +02:00
threadMessageHandler = std : : thread ( & util : : TraceThread , " msghand " , [ this ] { ThreadMessageHandler ( ) ; } ) ;
2010-08-29 18:58:15 +02:00
2020-11-18 17:13:27 +01:00
if ( connOptions . m_i2p_accept_incoming & & m_i2p_sam_session . get ( ) ! = nullptr ) {
threadI2PAcceptIncoming =
2023-08-26 11:50:37 +02:00
std : : thread ( & util : : TraceThread , " i2paccept " , [ this ] { ThreadI2PAcceptIncoming ( ) ; } ) ;
2020-11-18 17:13:27 +01:00
}
2012-01-04 23:39:45 +01:00
// Dump network addresses
2023-01-15 12:04:56 +01:00
scheduler . scheduleEvery ( [ this ] { DumpAddresses ( ) ; } , DUMP_PEERS_INTERVAL ) ;
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
2017-07-21 11:35:19 +02:00
return true ;
2010-08-29 18:58:15 +02:00
}
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
2017-07-21 11:35:19 +02:00
class CNetCleanup
2010-08-29 18:58:15 +02:00
{
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
2017-07-21 11:35:19 +02:00
public :
CNetCleanup ( ) { }
~ CNetCleanup ( )
{
# ifdef WIN32
// Shutdown Windows Sockets
WSACleanup ( ) ;
# endif
}
2019-05-26 11:01:58 +02:00
} ;
static CNetCleanup instance_of_cnetcleanup ;
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
2017-07-21 11:35:19 +02:00
void CExplicitNetCleanup : : callCleanup ( )
{
// Explicit call to destructor of CNetCleanup because it's not implicitly called
// when the wallet is restarted from within the wallet itself.
2024-01-02 04:21:51 +01:00
CNetCleanup tmp ;
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
2017-07-21 11:35:19 +02:00
}
2017-08-09 18:06:31 +02:00
void CConnman : : Interrupt ( )
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
2017-07-21 11:35:19 +02:00
{
2017-08-09 18:06:31 +02:00
{
2021-06-09 14:06:31 +02:00
LOCK ( mutexMsgProc ) ;
2017-08-09 18:06:31 +02:00
flagInterruptMsgProc = true ;
}
condMsgProc . notify_all ( ) ;
interruptNet ( ) ;
InterruptSocks5 ( true ) ;
2017-03-13 06:57:07 +01:00
if ( semOutbound ) {
2022-06-19 08:02:28 +02:00
for ( int i = 0 ; i < m_max_outbound ; i + + ) {
2012-05-12 17:44:14 +02:00
semOutbound - > post ( ) ;
2017-03-13 06:57:07 +01:00
}
}
if ( semAddnode ) {
for ( int i = 0 ; i < nMaxAddnode ; i + + ) {
semAddnode - > post ( ) ;
}
}
2017-08-09 18:06:31 +02:00
}
2023-04-27 10:48:14 +02:00
void CConnman : : StopThreads ( )
2017-08-09 18:06:31 +02:00
{
2020-11-18 17:13:27 +01:00
if ( threadI2PAcceptIncoming . joinable ( ) ) {
threadI2PAcceptIncoming . join ( ) ;
}
2017-08-09 18:06:31 +02:00
if ( threadMessageHandler . joinable ( ) )
threadMessageHandler . join ( ) ;
2018-02-01 02:10:52 +01:00
if ( threadOpenMasternodeConnections . joinable ( ) )
threadOpenMasternodeConnections . join ( ) ;
2017-08-09 18:06:31 +02:00
if ( threadOpenConnections . joinable ( ) )
threadOpenConnections . join ( ) ;
if ( threadOpenAddedConnections . joinable ( ) )
threadOpenAddedConnections . join ( ) ;
if ( threadDNSAddressSeed . joinable ( ) )
threadDNSAddressSeed . join ( ) ;
if ( threadSocketHandler . joinable ( ) )
threadSocketHandler . join ( ) ;
2023-04-27 10:48:14 +02:00
}
2014-09-18 14:08:43 +02:00
2023-04-27 10:48:14 +02:00
void CConnman : : StopNodes ( )
{
if ( fAddressesInitialized ) {
2019-01-21 18:45:59 +01:00
DumpAddresses ( ) ;
2014-09-18 14:08:43 +02:00
fAddressesInitialized = false ;
2023-09-10 19:02:53 +02:00
if ( m_use_addrman_outgoing ) {
// Anchor connections are only dumped during clean shutdown.
std : : vector < CAddress > anchors_to_dump = GetCurrentBlockRelayOnlyConns ( ) ;
if ( anchors_to_dump . size ( ) > MAX_BLOCK_RELAY_ONLY_ANCHORS ) {
anchors_to_dump . resize ( MAX_BLOCK_RELAY_ONLY_ANCHORS ) ;
}
DumpAnchors ( GetDataDir ( ) / ANCHORS_DATABASE_FILENAME , anchors_to_dump ) ;
}
2014-09-18 14:08:43 +02:00
}
2013-03-29 02:17:10 +01:00
2020-04-07 07:00:41 +02:00
{
LOCK ( cs_vNodes ) ;
// Close sockets
for ( CNode * pnode : vNodes )
pnode - > CloseSocketDisconnect ( this ) ;
}
2019-07-05 09:06:28 +02:00
for ( ListenSocket & hListenSocket : vhListenSocket )
2020-04-07 17:58:38 +02:00
if ( hListenSocket . socket ! = INVALID_SOCKET ) {
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode = = SOCKETEVENTS_KQUEUE ) {
struct kevent event ;
EV_SET ( & event , hListenSocket . socket , EVFILT_READ , EV_DELETE , 0 , 0 , nullptr ) ;
kevent ( kqueuefd , & event , 1 , nullptr , 0 , nullptr ) ;
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode = = SOCKETEVENTS_EPOLL ) {
epoll_ctl ( epollfd , EPOLL_CTL_DEL , hListenSocket . socket , nullptr ) ;
}
# endif
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
2017-07-21 11:35:19 +02:00
if ( ! CloseSocket ( hListenSocket . socket ) )
LogPrintf ( " CloseSocket(hListenSocket) failed with error %s \n " , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
2020-04-07 17:58:38 +02:00
}
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
2017-07-21 11:35:19 +02:00
// clean up some globals (to help leak detection)
2022-01-10 18:31:45 +01:00
std : : vector < CNode * > nodes ;
WITH_LOCK ( cs_vNodes , nodes . swap ( vNodes ) ) ;
2023-04-27 10:48:14 +02:00
for ( CNode * pnode : nodes ) {
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
2017-07-21 11:35:19 +02:00
DeleteNode ( pnode ) ;
}
2023-04-27 10:48:14 +02:00
for ( CNode * pnode : vNodesDisconnected ) {
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
2017-07-21 11:35:19 +02:00
DeleteNode ( pnode ) ;
}
2020-04-07 07:00:41 +02:00
mapSocketToNode . clear ( ) ;
2021-06-23 10:10:42 +02:00
{
LOCK ( cs_vNodes ) ;
mapReceivableNodes . clear ( ) ;
}
2020-04-07 17:52:20 +02:00
{
LOCK ( cs_mapNodesWithDataToSend ) ;
mapNodesWithDataToSend . clear ( ) ;
}
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
2017-07-21 11:35:19 +02:00
vNodesDisconnected . clear ( ) ;
vhListenSocket . clear ( ) ;
2017-11-09 21:22:08 +01:00
semOutbound . reset ( ) ;
semAddnode . reset ( ) ;
2019-02-14 18:50:18 +01:00
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode = = SOCKETEVENTS_KQUEUE & & kqueuefd ! = - 1 ) {
# ifdef USE_WAKEUP_PIPE
struct kevent event ;
EV_SET ( & event , wakeupPipe [ 0 ] , EVFILT_READ , EV_DELETE , 0 , 0 , nullptr ) ;
kevent ( kqueuefd , & event , 1 , nullptr , 0 , nullptr ) ;
# endif
close ( kqueuefd ) ;
}
kqueuefd = - 1 ;
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode = = SOCKETEVENTS_EPOLL & & epollfd ! = - 1 ) {
# ifdef USE_WAKEUP_PIPE
epoll_ctl ( epollfd , EPOLL_CTL_DEL , wakeupPipe [ 0 ] , nullptr ) ;
# endif
close ( epollfd ) ;
}
epollfd = - 1 ;
# endif
2020-04-06 07:18:50 +02:00
# ifdef USE_WAKEUP_PIPE
2019-02-14 18:50:18 +01:00
if ( wakeupPipe [ 0 ] ! = - 1 ) close ( wakeupPipe [ 0 ] ) ;
if ( wakeupPipe [ 1 ] ! = - 1 ) close ( wakeupPipe [ 1 ] ) ;
wakeupPipe [ 0 ] = wakeupPipe [ 1 ] = - 1 ;
# endif
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
2017-07-21 11:35:19 +02:00
}
void CConnman : : DeleteNode ( CNode * pnode )
{
assert ( pnode ) ;
2023-02-16 07:34:06 +01:00
m_msgproc - > FinalizeNode ( * pnode ) ;
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
2017-07-21 11:35:19 +02:00
delete pnode ;
}
CConnman : : ~ CConnman ( )
{
2017-08-09 18:06:31 +02:00
Interrupt ( ) ;
2017-07-28 00:39:53 +02:00
Stop ( ) ;
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
2017-07-21 11:35:19 +02:00
}
2021-05-02 18:44:17 +02:00
std : : vector < CAddress > CConnman : : GetAddresses ( size_t max_addresses , size_t max_pct , std : : optional < Network > network )
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
2017-07-21 11:35:19 +02:00
{
2021-05-02 18:44:17 +02:00
std : : vector < CAddress > addresses = addrman . GetAddr ( max_addresses , max_pct , network ) ;
2023-06-02 11:02:59 +02:00
if ( m_banman ) {
addresses . erase ( std : : remove_if ( addresses . begin ( ) , addresses . end ( ) ,
[ this ] ( const CAddress & addr ) { return m_banman - > IsDiscouraged ( addr ) | | m_banman - > IsBanned ( addr ) ; } ) ,
addresses . end ( ) ) ;
}
return addresses ;
}
2023-06-03 08:28:46 +02:00
std : : vector < CAddress > CConnman : : GetAddresses ( CNode & requestor , size_t max_addresses , size_t max_pct )
2023-06-02 11:02:59 +02:00
{
2020-11-18 17:13:27 +01:00
auto local_socket_bytes = requestor . addrBind . GetAddrBytes ( ) ;
2023-06-03 08:28:46 +02:00
uint64_t cache_id = GetDeterministicRandomizer ( RANDOMIZER_ID_ADDRCACHE )
. Write ( requestor . addr . GetNetwork ( ) )
. Write ( local_socket_bytes . data ( ) , local_socket_bytes . size ( ) )
. Finalize ( ) ;
2023-06-02 11:02:59 +02:00
const auto current_time = GetTime < std : : chrono : : microseconds > ( ) ;
2023-06-03 08:28:46 +02:00
auto r = m_addr_response_caches . emplace ( cache_id , CachedAddrResponse { } ) ;
CachedAddrResponse & cache_entry = r . first - > second ;
if ( cache_entry . m_cache_entry_expiration < current_time ) { // If emplace() added new one it has expiration 0.
2021-05-02 18:44:17 +02:00
cache_entry . m_addrs_response_cache = GetAddresses ( max_addresses , max_pct , /* network */ std : : nullopt ) ;
2023-06-03 08:28:46 +02:00
// Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
// and the usefulness of ADDR responses to honest users.
//
// Longer cache lifetime makes it more difficult for an attacker to scrape
// enough AddrMan data to maliciously infer something useful.
// By the time an attacker scraped enough AddrMan records, most of
// the records should be old enough to not leak topology info by
// e.g. analyzing real-time changes in timestamps.
//
// It takes only several hundred requests to scrape everything from an AddrMan containing 100,000 nodes,
// so ~24 hours of cache lifetime indeed makes the data less inferable by the time
// most of it could be scraped (considering that timestamps are updated via
// ADDR self-announcements and when nodes communicate).
// We also should be robust to those attacks which may not require scraping *full* victim's AddrMan
// (because even several timestamps of the same handful of nodes may leak privacy).
//
// On the other hand, longer cache lifetime makes ADDR responses
// outdated and less useful for an honest requestor, e.g. if most nodes
// in the ADDR response are no longer active.
//
// However, the churn in the network is known to be rather low. Since we consider
// nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days,
// max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference
// in terms of the freshness of the response.
cache_entry . m_cache_entry_expiration = current_time + std : : chrono : : hours ( 21 ) + GetRandMillis ( std : : chrono : : hours ( 6 ) ) ;
2023-06-02 11:02:59 +02:00
}
2023-06-03 08:28:46 +02:00
return cache_entry . m_addrs_response_cache ;
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
2017-07-21 11:35:19 +02:00
}
bool CConnman : : AddNode ( const std : : string & strNode )
{
LOCK ( cs_vAddedNodes ) ;
2017-09-21 01:32:56 +02:00
for ( const std : : string & it : vAddedNodes ) {
if ( strNode = = it ) return false ;
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
2017-07-21 11:35:19 +02:00
}
vAddedNodes . push_back ( strNode ) ;
2010-08-29 18:58:15 +02:00
return true ;
}
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
2017-07-21 11:35:19 +02:00
bool CConnman : : RemoveAddedNode ( const std : : string & strNode )
2010-08-29 18:58:15 +02:00
{
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
2017-07-21 11:35:19 +02:00
LOCK ( cs_vAddedNodes ) ;
for ( std : : vector < std : : string > : : iterator it = vAddedNodes . begin ( ) ; it ! = vAddedNodes . end ( ) ; + + it ) {
if ( strNode = = * it ) {
vAddedNodes . erase ( it ) ;
return true ;
}
}
return false ;
}
2014-05-24 11:14:52 +02:00
2020-03-19 14:21:02 +01:00
bool CConnman : : AddPendingMasternode ( const uint256 & proTxHash )
2018-02-01 02:10:52 +01:00
{
LOCK ( cs_vPendingMasternodes ) ;
2020-03-19 14:21:02 +01:00
if ( std : : find ( vPendingMasternodes . begin ( ) , vPendingMasternodes . end ( ) , proTxHash ) ! = vPendingMasternodes . end ( ) ) {
return false ;
2018-02-01 02:10:52 +01:00
}
2020-03-19 14:21:02 +01:00
vPendingMasternodes . push_back ( proTxHash ) ;
2018-02-01 02:10:52 +01:00
return true ;
}
2020-03-16 11:06:29 +01:00
void CConnman : : SetMasternodeQuorumNodes ( Consensus : : LLMQType llmqType , const uint256 & quorumHash , const std : : set < uint256 > & proTxHashes )
2018-05-24 16:15:52 +02:00
{
LOCK ( cs_vPendingMasternodes ) ;
2020-03-16 11:06:29 +01:00
auto it = masternodeQuorumNodes . emplace ( std : : make_pair ( llmqType , quorumHash ) , proTxHashes ) ;
if ( ! it . second ) {
it . first - > second = proTxHashes ;
2018-05-24 16:15:52 +02:00
}
}
2021-03-15 03:49:38 +01:00
void CConnman : : SetMasternodeQuorumRelayMembers ( Consensus : : LLMQType llmqType , const uint256 & quorumHash , const std : : set < uint256 > & proTxHashes )
{
{
LOCK ( cs_vPendingMasternodes ) ;
auto it = masternodeQuorumRelayMembers . emplace ( std : : make_pair ( llmqType , quorumHash ) , proTxHashes ) ;
if ( ! it . second ) {
it . first - > second = proTxHashes ;
}
}
// Update existing connections
ForEachNode ( [ & ] ( CNode * pnode ) {
2021-07-26 17:52:52 +02:00
auto verifiedProRegTxHash = pnode - > GetVerifiedProRegTxHash ( ) ;
if ( ! verifiedProRegTxHash . IsNull ( ) & & ! pnode - > m_masternode_iqr_connection & & IsMasternodeQuorumRelayMember ( verifiedProRegTxHash ) ) {
2021-03-15 03:49:38 +01:00
// Tell our peer that we're interested in plain LLMQ recovered signatures.
// Otherwise the peer would only announce/send messages resulting from QRECSIG,
// e.g. InstantSend locks or ChainLocks. SPV and regular full nodes should not send
// this message as they are usually only interested in the higher level messages.
const CNetMsgMaker msgMaker ( pnode - > GetSendVersion ( ) ) ;
PushMessage ( pnode , msgMaker . Make ( NetMsgType : : QSENDRECSIGS , true ) ) ;
pnode - > m_masternode_iqr_connection = true ;
}
} ) ;
}
2018-05-24 16:15:52 +02:00
bool CConnman : : HasMasternodeQuorumNodes ( Consensus : : LLMQType llmqType , const uint256 & quorumHash )
{
LOCK ( cs_vPendingMasternodes ) ;
return masternodeQuorumNodes . count ( std : : make_pair ( llmqType , quorumHash ) ) ;
}
std : : set < uint256 > CConnman : : GetMasternodeQuorums ( Consensus : : LLMQType llmqType )
{
LOCK ( cs_vPendingMasternodes ) ;
std : : set < uint256 > result ;
2019-01-11 10:00:40 +01:00
for ( const auto & p : masternodeQuorumNodes ) {
2018-05-24 16:15:52 +02:00
if ( p . first . first ! = llmqType ) {
continue ;
}
result . emplace ( p . first . second ) ;
}
return result ;
}
std : : set < NodeId > CConnman : : GetMasternodeQuorumNodes ( Consensus : : LLMQType llmqType , const uint256 & quorumHash ) const
{
LOCK2 ( cs_vNodes , cs_vPendingMasternodes ) ;
auto it = masternodeQuorumNodes . find ( std : : make_pair ( llmqType , quorumHash ) ) ;
if ( it = = masternodeQuorumNodes . end ( ) ) {
return { } ;
}
2019-04-09 12:22:46 +02:00
const auto & proRegTxHashes = it - > second ;
2019-03-22 15:21:34 +01:00
2018-05-24 16:15:52 +02:00
std : : set < NodeId > nodes ;
2019-01-11 10:00:40 +01:00
for ( const auto pnode : vNodes ) {
2018-05-24 16:15:52 +02:00
if ( pnode - > fDisconnect ) {
continue ;
}
2021-07-26 17:52:52 +02:00
auto verifiedProRegTxHash = pnode - > GetVerifiedProRegTxHash ( ) ;
if ( ! pnode - > qwatch & & ( verifiedProRegTxHash . IsNull ( ) | | ! proRegTxHashes . count ( verifiedProRegTxHash ) ) ) {
2018-05-24 16:15:52 +02:00
continue ;
}
2017-05-07 09:59:42 +02:00
nodes . emplace ( pnode - > GetId ( ) ) ;
2018-05-24 16:15:52 +02:00
}
return nodes ;
}
void CConnman : : RemoveMasternodeQuorumNodes ( Consensus : : LLMQType llmqType , const uint256 & quorumHash )
{
LOCK ( cs_vPendingMasternodes ) ;
masternodeQuorumNodes . erase ( std : : make_pair ( llmqType , quorumHash ) ) ;
2021-03-15 03:49:38 +01:00
masternodeQuorumRelayMembers . erase ( std : : make_pair ( llmqType , quorumHash ) ) ;
2018-05-24 16:15:52 +02:00
}
2019-03-22 15:21:34 +01:00
bool CConnman : : IsMasternodeQuorumNode ( const CNode * pnode )
2018-05-24 16:15:52 +02:00
{
2019-04-09 12:22:46 +02:00
// Let's see if this is an outgoing connection to an address that is known to be a masternode
// We however only need to know this if the node did not authenticate itself as a MN yet
uint256 assumedProTxHash ;
2021-07-26 17:52:52 +02:00
if ( pnode - > GetVerifiedProRegTxHash ( ) . IsNull ( ) & & ! pnode - > fInbound ) {
2019-04-09 12:22:46 +02:00
auto mnList = deterministicMNManager - > GetListAtChainTip ( ) ;
2019-06-13 11:03:20 +02:00
auto dmn = mnList . GetMNByService ( pnode - > addr ) ;
2019-04-09 12:22:46 +02:00
if ( dmn = = nullptr ) {
// This is definitely not a masternode
return false ;
}
assumedProTxHash = dmn - > proTxHash ;
}
2018-05-24 16:15:52 +02:00
LOCK ( cs_vPendingMasternodes ) ;
for ( const auto & p : masternodeQuorumNodes ) {
2021-07-26 17:52:52 +02:00
if ( ! pnode - > GetVerifiedProRegTxHash ( ) . IsNull ( ) ) {
if ( p . second . count ( pnode - > GetVerifiedProRegTxHash ( ) ) ) {
2019-03-22 15:21:34 +01:00
return true ;
}
2019-04-09 12:22:46 +02:00
} else if ( ! assumedProTxHash . IsNull ( ) ) {
if ( p . second . count ( assumedProTxHash ) ) {
2019-03-22 15:21:34 +01:00
return true ;
}
2018-05-24 16:15:52 +02:00
}
}
return false ;
}
2021-03-15 03:49:38 +01:00
bool CConnman : : IsMasternodeQuorumRelayMember ( const uint256 & protxHash )
{
if ( protxHash . IsNull ( ) ) {
return false ;
}
LOCK ( cs_vPendingMasternodes ) ;
for ( const auto & p : masternodeQuorumRelayMembers ) {
if ( p . second . count ( protxHash ) ) {
return true ;
}
}
return false ;
}
2020-03-17 10:04:31 +01:00
void CConnman : : AddPendingProbeConnections ( const std : : set < uint256 > & proTxHashes )
{
LOCK ( cs_vPendingMasternodes ) ;
masternodePendingProbes . insert ( proTxHashes . begin ( ) , proTxHashes . end ( ) ) ;
}
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
2017-07-21 11:35:19 +02:00
size_t CConnman : : GetNodeCount ( NumConnections flags )
{
LOCK ( cs_vNodes ) ;
2013-03-29 02:17:10 +01:00
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
2017-07-21 11:35:19 +02:00
int nNum = 0 ;
2017-09-21 01:32:56 +02:00
for ( const auto & pnode : vNodes ) {
2020-04-21 18:26:15 +02:00
if ( pnode - > fDisconnect ) {
continue ;
}
2022-07-14 23:13:47 +02:00
if ( ( flags & CONNECTIONS_VERIFIED ) & & pnode - > GetVerifiedProRegTxHash ( ) . IsNull ( ) ) {
continue ;
}
2017-09-21 01:32:56 +02:00
if ( flags & ( pnode - > fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT ) ) {
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
2017-07-21 11:35:19 +02:00
nNum + + ;
2022-07-25 23:38:07 +02:00
} else if ( flags = = CONNECTIONS_VERIFIED ) {
nNum + + ;
2017-09-21 01:32:56 +02:00
}
}
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
2017-07-21 11:35:19 +02:00
return nNum ;
}
2020-01-01 15:13:14 +01:00
size_t CConnman : : GetMaxOutboundNodeCount ( )
{
2022-06-19 08:02:28 +02:00
return m_max_outbound ;
2020-01-01 15:13:14 +01:00
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : GetNodeStats ( std : : vector < CNodeStats > & vstats )
{
vstats . clear ( ) ;
LOCK ( cs_vNodes ) ;
vstats . reserve ( vNodes . size ( ) ) ;
2017-09-21 01:32:56 +02:00
for ( CNode * pnode : vNodes ) {
2020-04-21 18:26:15 +02:00
if ( pnode - > fDisconnect ) {
continue ;
}
2017-02-11 00:53:31 +01:00
vstats . emplace_back ( ) ;
2021-05-11 17:11:07 +02:00
pnode - > copyStats ( vstats . back ( ) , addrman . m_asmap ) ;
2010-08-29 18:58:15 +02:00
}
}
2012-08-13 05:26:30 +02:00
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
2017-07-21 11:35:19 +02:00
bool CConnman : : DisconnectNode ( const std : : string & strNode )
{
2017-08-29 01:51:56 +02:00
LOCK ( cs_vNodes ) ;
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
2017-07-21 11:35:19 +02:00
if ( CNode * pnode = FindNode ( strNode ) ) {
pnode - > fDisconnect = true ;
return true ;
}
return false ;
}
2019-01-21 18:45:59 +01:00
bool CConnman : : DisconnectNode ( const CSubNet & subnet )
{
bool disconnected = false ;
LOCK ( cs_vNodes ) ;
for ( CNode * pnode : vNodes ) {
if ( subnet . Match ( pnode - > addr ) ) {
pnode - > fDisconnect = true ;
disconnected = true ;
}
}
return disconnected ;
}
bool CConnman : : DisconnectNode ( const CNetAddr & addr )
{
return DisconnectNode ( CSubNet ( addr ) ) ;
}
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
2017-07-21 11:35:19 +02:00
bool CConnman : : DisconnectNode ( NodeId id )
{
LOCK ( cs_vNodes ) ;
for ( CNode * pnode : vNodes ) {
2017-05-07 09:59:42 +02:00
if ( id = = pnode - > GetId ( ) ) {
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
2017-07-21 11:35:19 +02:00
pnode - > fDisconnect = true ;
return true ;
}
}
return false ;
2015-05-25 22:59:38 +02:00
}
2012-08-13 05:26:30 +02:00
2016-05-16 22:47:29 +02:00
void CConnman : : RelayTransaction ( const CTransaction & tx )
2012-08-13 05:26:30 +02:00
{
2016-05-24 01:06:09 +02:00
uint256 hash = tx . GetHash ( ) ;
2019-02-28 13:44:53 +01:00
int nInv = MSG_TX ;
2021-03-17 23:36:11 +01:00
if ( CCoinJoin : : GetDSTX ( hash ) ) {
2019-02-28 13:44:53 +01:00
nInv = MSG_DSTX ;
}
2016-05-24 01:06:09 +02:00
CInv inv ( nInv , hash ) ;
2021-04-03 19:24:03 +02:00
RelayInv ( inv ) ;
2012-08-13 05:26:30 +02:00
}
2013-08-22 18:09:32 +02:00
2021-04-03 19:24:03 +02:00
void CConnman : : RelayInv ( CInv & inv , const int minProtoVersion ) {
2015-07-08 02:37:23 +02:00
LOCK ( cs_vNodes ) ;
2020-03-17 12:20:05 +01:00
for ( const auto & pnode : vNodes ) {
2021-04-03 19:24:03 +02:00
if ( pnode - > nVersion < minProtoVersion | | ! pnode - > CanRelay ( ) )
2020-03-17 12:20:05 +01:00
continue ;
pnode - > PushInventory ( inv ) ;
}
2015-07-08 02:37:23 +02:00
}
2021-04-03 19:24:03 +02:00
void CConnman : : RelayInvFiltered ( CInv & inv , const CTransaction & relatedTx , const int minProtoVersion )
2018-07-20 15:32:41 +02:00
{
LOCK ( cs_vNodes ) ;
for ( const auto & pnode : vNodes ) {
2023-04-19 16:57:27 +02:00
if ( pnode - > nVersion < minProtoVersion | | ! pnode - > CanRelay ( ) | | ! pnode - > IsAddrRelayPeer ( ) ) {
2018-07-20 15:32:41 +02:00
continue ;
2022-07-07 17:11:38 +02:00
}
{
2022-06-19 08:02:28 +02:00
LOCK ( pnode - > m_tx_relay - > cs_filter ) ;
2022-07-07 17:11:38 +02:00
if ( ! pnode - > m_tx_relay - > fRelayTxes ) {
2018-07-20 15:32:41 +02:00
continue ;
2022-07-07 17:11:38 +02:00
}
if ( pnode - > m_tx_relay - > pfilter & & ! pnode - > m_tx_relay - > pfilter - > IsRelevantAndUpdate ( relatedTx ) ) {
continue ;
}
2018-07-20 15:32:41 +02:00
}
pnode - > PushInventory ( inv ) ;
}
}
2021-04-03 19:24:03 +02:00
void CConnman : : RelayInvFiltered ( CInv & inv , const uint256 & relatedTxHash , const int minProtoVersion )
2018-11-23 15:40:19 +01:00
{
LOCK ( cs_vNodes ) ;
for ( const auto & pnode : vNodes ) {
2023-04-19 16:57:27 +02:00
if ( pnode - > nVersion < minProtoVersion | | ! pnode - > CanRelay ( ) | | ! pnode - > IsAddrRelayPeer ( ) ) {
2020-03-17 12:20:05 +01:00
continue ;
2022-07-07 17:11:38 +02:00
}
{
2022-06-19 08:02:28 +02:00
LOCK ( pnode - > m_tx_relay - > cs_filter ) ;
2022-07-07 17:11:38 +02:00
if ( ! pnode - > m_tx_relay - > fRelayTxes ) {
continue ;
}
if ( pnode - > m_tx_relay - > pfilter & & ! pnode - > m_tx_relay - > pfilter - > contains ( relatedTxHash ) ) {
continue ;
}
2018-11-23 15:40:19 +01:00
}
pnode - > PushInventory ( inv ) ;
}
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : RecordBytesRecv ( uint64_t bytes )
2013-08-22 18:09:32 +02:00
{
LOCK ( cs_totalBytesRecv ) ;
nTotalBytesRecv + = bytes ;
2020-12-15 17:22:23 +01:00
statsClient . count ( " bandwidth.bytesReceived " , bytes , 0.1f ) ;
statsClient . gauge ( " bandwidth.totalBytesReceived " , nTotalBytesRecv , 0.01f ) ;
2013-08-22 18:09:32 +02:00
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : RecordBytesSent ( uint64_t bytes )
2013-08-22 18:09:32 +02:00
{
LOCK ( cs_totalBytesSent ) ;
nTotalBytesSent + = bytes ;
2020-12-15 17:22:23 +01:00
statsClient . count ( " bandwidth.bytesSent " , bytes , 0.01f ) ;
statsClient . gauge ( " bandwidth.totalBytesSent " , nTotalBytesSent , 0.01f ) ;
2015-09-02 17:03:27 +02:00
2022-10-15 23:50:01 +02:00
const auto now = GetTime < std : : chrono : : seconds > ( ) ;
if ( nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME < now )
2015-09-02 17:03:27 +02:00
{
// timeframe expired, reset cycle
nMaxOutboundCycleStartTime = now ;
nMaxOutboundTotalBytesSentInCycle = 0 ;
}
nMaxOutboundTotalBytesSentInCycle + = bytes ;
}
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
2017-07-21 11:35:19 +02:00
uint64_t CConnman : : GetMaxOutboundTarget ( )
2015-09-02 17:03:27 +02:00
{
LOCK ( cs_totalBytesSent ) ;
return nMaxOutboundLimit ;
}
2022-10-15 23:50:01 +02:00
std : : chrono : : seconds CConnman : : GetMaxOutboundTimeframe ( )
2015-09-02 17:03:27 +02:00
{
2022-10-15 23:50:01 +02:00
return MAX_UPLOAD_TIMEFRAME ;
2015-09-02 17:03:27 +02:00
}
2022-10-15 23:50:01 +02:00
std : : chrono : : seconds CConnman : : GetMaxOutboundTimeLeftInCycle ( )
2015-09-02 17:03:27 +02:00
{
LOCK ( cs_totalBytesSent ) ;
if ( nMaxOutboundLimit = = 0 )
2022-10-15 23:50:01 +02:00
return 0 s ;
2015-09-02 17:03:27 +02:00
2022-10-15 23:50:01 +02:00
if ( nMaxOutboundCycleStartTime . count ( ) = = 0 )
return MAX_UPLOAD_TIMEFRAME ;
2015-09-02 17:03:27 +02:00
2022-10-15 23:50:01 +02:00
const std : : chrono : : seconds cycleEndTime = nMaxOutboundCycleStartTime + MAX_UPLOAD_TIMEFRAME ;
const auto now = GetTime < std : : chrono : : seconds > ( ) ;
return ( cycleEndTime < now ) ? 0 s : cycleEndTime - now ;
2015-09-02 17:03:27 +02:00
}
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
2017-07-21 11:35:19 +02:00
bool CConnman : : OutboundTargetReached ( bool historicalBlockServingLimit )
2015-09-02 17:03:27 +02:00
{
LOCK ( cs_totalBytesSent ) ;
if ( nMaxOutboundLimit = = 0 )
return false ;
if ( historicalBlockServingLimit )
{
2017-09-07 17:59:00 +02:00
// keep a large enough buffer to at least relay each block once
2022-10-15 23:50:01 +02:00
const std : : chrono : : seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle ( ) ;
const uint64_t buffer = timeLeftInCycle / std : : chrono : : minutes { 10 } * MaxBlockSize ( fDIP0001ActiveAtTip ) ;
2015-09-02 17:03:27 +02:00
if ( buffer > = nMaxOutboundLimit | | nMaxOutboundTotalBytesSentInCycle > = nMaxOutboundLimit - buffer )
return true ;
}
else if ( nMaxOutboundTotalBytesSentInCycle > = nMaxOutboundLimit )
return true ;
return false ;
}
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
2017-07-21 11:35:19 +02:00
uint64_t CConnman : : GetOutboundTargetBytesLeft ( )
2015-09-02 17:03:27 +02:00
{
LOCK ( cs_totalBytesSent ) ;
if ( nMaxOutboundLimit = = 0 )
return 0 ;
return ( nMaxOutboundTotalBytesSentInCycle > = nMaxOutboundLimit ) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle ;
2013-08-22 18:09:32 +02:00
}
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
2017-07-21 11:35:19 +02:00
uint64_t CConnman : : GetTotalBytesRecv ( )
2013-08-22 18:09:32 +02:00
{
LOCK ( cs_totalBytesRecv ) ;
return nTotalBytesRecv ;
}
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
2017-07-21 11:35:19 +02:00
uint64_t CConnman : : GetTotalBytesSent ( )
2013-08-22 18:09:32 +02:00
{
LOCK ( cs_totalBytesSent ) ;
return nTotalBytesSent ;
}
2013-10-28 07:28:00 +01:00
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
2017-07-21 11:35:19 +02:00
ServiceFlags CConnman : : GetLocalServices ( ) const
{
return nLocalServices ;
}
unsigned int CConnman : : GetReceiveFloodSize ( ) const { return nReceiveFloodSize ; }
2013-11-29 16:33:34 +01:00
2023-04-28 08:36:19 +02:00
CNode : : CNode ( NodeId idIn , ServiceFlags nLocalServicesIn , SOCKET hSocketIn , const CAddress & addrIn , uint64_t nKeyedNetGroupIn , uint64_t nLocalHostNonceIn , const CAddress & addrBindIn , const std : : string & addrNameIn , bool fInboundIn , bool block_relay_only , bool inbound_onion )
2021-08-31 18:53:54 +02:00
: nTimeConnected ( GetSystemTimeInSeconds ( ) ) ,
2016-06-08 18:05:01 +02:00
addr ( addrIn ) ,
2017-06-05 15:39:11 +02:00
addrBind ( addrBindIn ) ,
2016-11-03 10:45:11 +01:00
fInbound ( fInboundIn ) ,
2016-09-19 17:05:35 +02:00
nKeyedNetGroup ( nKeyedNetGroupIn ) ,
2019-11-04 17:17:02 +01:00
m_addr_known { block_relay_only ? nullptr : std : : make_unique < CRollingBloomFilter > ( 5000 , 0.001 ) } ,
2017-05-07 09:59:42 +02:00
id ( idIn ) ,
2016-11-03 10:45:11 +01:00
nLocalHostNonce ( nLocalHostNonceIn ) ,
nLocalServices ( nLocalServicesIn ) ,
2023-04-16 06:56:24 +02:00
m_inbound_onion ( inbound_onion )
2014-08-21 05:17:21 +02:00
{
hSocket = hSocketIn ;
addrName = addrNameIn = = " " ? addr . ToStringIPPort ( ) : addrNameIn ;
2014-12-15 09:11:16 +01:00
hashContinue = uint256 ( ) ;
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
2017-07-21 11:35:19 +02:00
2019-07-05 09:06:28 +02:00
for ( const std : : string & msg : getAllNetMessageTypes ( ) )
2017-06-29 03:51:10 +02:00
mapRecvBytesPerMsgCmd [ msg ] = 0 ;
mapRecvBytesPerMsgCmd [ NET_MESSAGE_COMMAND_OTHER ] = 0 ;
2014-08-21 05:17:21 +02:00
2019-05-22 23:51:39 +02:00
if ( fLogIPs ) {
LogPrint ( BCLog : : NET , " Added connection to %s peer=%d \n " , addrName , id ) ;
} else {
LogPrint ( BCLog : : NET , " Added connection peer=%d \n " , id ) ;
}
2019-06-13 10:39:44 +02:00
2022-10-15 20:35:50 +02:00
m_deserializer = std : : make_unique < V1TransportDeserializer > ( V1TransportDeserializer ( Params ( ) , GetId ( ) , SER_NETWORK , INIT_PROTO_VERSION ) ) ;
m_serializer = std : : make_unique < V1TransportSerializer > ( V1TransportSerializer ( ) ) ;
2014-08-21 05:17:21 +02:00
}
CNode : : ~ CNode ( )
{
CloseSocket ( hSocket ) ;
}
2017-08-17 20:37:22 +02:00
bool CConnman : : NodeFullyConnected ( const CNode * pnode )
{
return pnode & & pnode - > fSuccessfullyConnected & & ! pnode - > fDisconnect ;
}
2020-04-17 11:09:34 +02:00
void CConnman : : PushMessage ( CNode * pnode , CSerializedNetMsg & & msg )
2017-07-27 16:28:05 +02:00
{
2016-11-25 20:01:56 +01:00
size_t nMessageSize = msg . data . size ( ) ;
2022-03-21 18:28:10 +01:00
LogPrint ( BCLog : : NET , " sending %s (%d bytes) peer=%d \n " , SanitizeString ( msg . command ) , nMessageSize , pnode - > GetId ( ) ) ;
2017-07-27 16:28:05 +02:00
2019-08-07 15:56:24 +02:00
// make sure we use the appropriate network transport format
2016-11-25 20:01:56 +01:00
std : : vector < unsigned char > serializedHeader ;
2019-08-07 15:56:24 +02:00
pnode - > m_serializer - > prepareForTransport ( msg , serializedHeader ) ;
2017-07-27 16:28:05 +02:00
2019-08-07 15:56:24 +02:00
size_t nTotalSize = nMessageSize + serializedHeader . size ( ) ;
statsClient . count ( " bandwidth.message. " + SanitizeString ( msg . command . c_str ( ) ) + " .bytesSent " , nTotalSize , 1.0f ) ;
statsClient . inc ( " message.sent. " + SanitizeString ( msg . command . c_str ( ) ) , 1.0f ) ;
2017-07-27 16:28:05 +02:00
{
LOCK ( pnode - > cs_vSend ) ;
2019-04-11 14:43:22 +02:00
bool hasPendingData = ! pnode - > vSendMsg . empty ( ) ;
2017-07-27 16:28:05 +02:00
//log total amount of bytes per command
2016-11-25 20:01:56 +01:00
pnode - > mapSendBytesPerMsgCmd [ msg . command ] + = nTotalSize ;
pnode - > nSendSize + = nTotalSize ;
2017-07-27 16:28:05 +02:00
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
if ( pnode - > nSendSize > nSendBufferMaxSize )
pnode - > fPauseSend = true ;
2016-11-25 20:01:56 +01:00
pnode - > vSendMsg . push_back ( std : : move ( serializedHeader ) ) ;
if ( nMessageSize )
pnode - > vSendMsg . push_back ( std : : move ( msg . data ) ) ;
2020-04-07 17:53:26 +02:00
pnode - > nSendMsgSize = pnode - > vSendMsg . size ( ) ;
Backport Bitcoin PR#9441: Net: Massive speedup. Net locks overhaul (#1586)
* net: fix typo causing the wrong receive buffer size
Surprisingly this hasn't been causing me any issues while testing, probably
because it requires lots of large blocks to be flying around.
Send/Recv corks need tests!
* net: make vRecvMsg a list so that we can use splice()
* net: make GetReceiveFloodSize public
This will be needed so that the message processor can cork incoming messages
* net: only disconnect if fDisconnect has been set
These conditions are problematic to check without locking, and we shouldn't be
relying on the refcount to disconnect.
* net: wait until the node is destroyed to delete its recv buffer
when vRecvMsg becomes a private buffer, it won't make sense to allow other
threads to mess with it anymore.
* net: set message deserialization version when it's actually time to deserialize
We'll soon no longer have access to vRecvMsg, and this is more intuitive anyway.
* net: handle message accounting in ReceiveMsgBytes
This allows locking to be pushed down to only where it's needed
Also reuse the current time rather than checking multiple times.
* net: record bytes written before notifying the message processor
* net: Add a simple function for waking the message handler
This may be used publicly in the future
* net: remove useless comments
* net: remove redundant max sendbuffer size check
This is left-over from before there was proper accounting. Hitting 2x the
sendbuffer size should not be possible.
* net: rework the way that the messagehandler sleeps
In order to sleep accurately, the message handler needs to know if _any_ node
has more processing that it should do before the entire thread sleeps.
Rather than returning a value that represents whether ProcessMessages
encountered a message that should trigger a disconnnect, interpret the return
value as whether or not that node has more work to do.
Also, use a global fProcessWake value that can be set by other threads,
which takes precedence (for one cycle) over the messagehandler's decision.
Note that the previous behavior was to only process one message per loop
(except in the case of a bad checksum or invalid header). That was changed in
PR #3180.
The only change here in that regard is that the current node now falls to the
back of the processing queue for the bad checksum/invalid header cases.
* net: add a new message queue for the message processor
This separates the storage of messages from the net and queued messages for
processing, allowing the locks to be split.
* net: add a flag to indicate when a node's process queue is full
Messages are dumped very quickly from the socket handler to the processor, so
it's the depth of the processing queue that's interesting.
The socket handler checks the process queue's size during the brief message
hand-off and pauses if necessary, and the processor possibly unpauses each time
a message is popped off of its queue.
* net: add a flag to indicate when a node's send buffer is full
Similar to the recv flag, but this one indicates whether or not the net's send
buffer is full.
The socket handler checks the send queue when a new message is added and pauses
if necessary, and possibly unpauses after each message is drained from its buffer.
* net: remove cs_vRecvMsg
vRecvMsg is now only touched by the socket handler thread.
The accounting vars (nRecvBytes/nLastRecv/mapRecvBytesPerMsgCmd) are also
only used by the socket handler thread, with the exception of queries from
rpc/gui. These accesses are not threadsafe, but they never were. This needs to
be addressed separately.
Also, update comment describing data flow
2017-08-23 16:20:43 +02:00
2020-04-07 17:52:20 +02:00
{
LOCK ( cs_mapNodesWithDataToSend ) ;
2020-04-17 23:07:17 +02:00
// we're not holding cs_vNodes here, so there is a chance of this node being disconnected shortly before
// we get here. Whoever called PushMessage still has a ref to CNode*, but will later Release() it, so we
// might end up having an entry in mapNodesWithDataToSend that is not in vNodes anymore. We need to
// Add/Release refs when adding/erasing mapNodesWithDataToSend.
if ( mapNodesWithDataToSend . emplace ( pnode - > GetId ( ) , pnode ) . second ) {
pnode - > AddRef ( ) ;
}
2020-04-07 17:52:20 +02:00
}
2019-04-11 14:43:22 +02:00
// wake up select() call in case there was no pending data before (so it was not selecting this socket for sending)
2020-04-17 11:09:34 +02:00
if ( ! hasPendingData & & wakeupSelectNeeded )
2019-04-11 14:43:22 +02:00
WakeSelect ( ) ;
2017-07-27 16:28:05 +02:00
}
}
2017-08-17 20:37:22 +02:00
bool CConnman : : ForNode ( const CService & addr , std : : function < bool ( const CNode * pnode ) > cond , std : : function < bool ( CNode * pnode ) > func )
2015-06-19 15:27:37 +02:00
{
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
2017-07-21 11:35:19 +02:00
CNode * found = nullptr ;
LOCK ( cs_vNodes ) ;
for ( auto & & pnode : vNodes ) {
if ( ( CService ) pnode - > addr = = addr ) {
found = pnode ;
break ;
}
2015-06-19 15:27:37 +02:00
}
2017-08-17 20:37:22 +02:00
return found ! = nullptr & & cond ( found ) & & func ( found ) ;
2015-06-19 15:27:37 +02:00
}
2017-08-17 20:37:22 +02:00
bool CConnman : : ForNode ( NodeId id , std : : function < bool ( const CNode * pnode ) > cond , std : : function < bool ( CNode * pnode ) > func )
2015-06-19 15:27:37 +02:00
{
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
2017-07-21 11:35:19 +02:00
CNode * found = nullptr ;
LOCK ( cs_vNodes ) ;
for ( auto & & pnode : vNodes ) {
2017-05-07 09:59:42 +02:00
if ( pnode - > GetId ( ) = = id ) {
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
2017-07-21 11:35:19 +02:00
found = pnode ;
break ;
}
2015-06-19 15:27:37 +02:00
}
2017-08-17 20:37:22 +02:00
return found ! = nullptr & & cond ( found ) & & func ( found ) ;
2015-06-19 15:27:37 +02:00
}
2018-01-17 16:09:08 +01:00
bool CConnman : : IsMasternodeOrDisconnectRequested ( const CService & addr ) {
return ForNode ( addr , AllNodes , [ ] ( CNode * pnode ) {
2021-01-14 20:59:18 +01:00
return pnode - > m_masternode_connection | | pnode - > fDisconnect ;
2018-01-17 16:09:08 +01:00
} ) ;
}
2018-07-16 19:28:42 +02:00
int64_t CConnman : : PoissonNextSendInbound ( int64_t now , int average_interval_seconds )
{
if ( m_next_send_inv_to_incoming < now ) {
// If this function were called from multiple threads simultaneously
// it would possible that both update the next send variable, and return a different result to their caller.
// This is not possible in practice as only the net processing thread invokes this function.
m_next_send_inv_to_incoming = PoissonNextSend ( now , average_interval_seconds ) ;
}
return m_next_send_inv_to_incoming ;
}
int64_t PoissonNextSend ( int64_t now , int average_interval_seconds )
{
return now + ( int64_t ) ( log1p ( GetRand ( 1ULL < < 48 ) * - 0.0000000000000035527136788 /* -1/2^48 */ ) * average_interval_seconds * - 1000000.0 + 0.5 ) ;
2015-04-08 20:20:00 +02:00
}
2017-03-05 20:16:12 +01:00
2018-01-22 14:17:11 +01:00
std : : vector < CNode * > CConnman : : CopyNodeVector ( std : : function < bool ( const CNode * pnode ) > cond )
2017-03-05 20:16:12 +01:00
{
std : : vector < CNode * > vecNodesCopy ;
LOCK ( cs_vNodes ) ;
2020-04-07 07:01:21 +02:00
vecNodesCopy . reserve ( vNodes . size ( ) ) ;
2017-03-05 20:16:12 +01:00
for ( size_t i = 0 ; i < vNodes . size ( ) ; + + i ) {
CNode * pnode = vNodes [ i ] ;
2018-01-22 14:17:11 +01:00
if ( ! cond ( pnode ) )
continue ;
2017-03-05 20:16:12 +01:00
pnode - > AddRef ( ) ;
vecNodesCopy . push_back ( pnode ) ;
}
return vecNodesCopy ;
}
2018-01-22 14:17:11 +01:00
std : : vector < CNode * > CConnman : : CopyNodeVector ( )
{
return CopyNodeVector ( AllNodes ) ;
}
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
2017-07-21 11:35:19 +02:00
void CConnman : : ReleaseNodeVector ( const std : : vector < CNode * > & vecNodes )
2017-03-05 20:16:12 +01:00
{
for ( size_t i = 0 ; i < vecNodes . size ( ) ; + + i ) {
CNode * pnode = vecNodes [ i ] ;
pnode - > Release ( ) ;
}
}
2016-06-08 18:05:01 +02:00
2017-02-06 14:31:37 +01:00
CSipHasher CConnman : : GetDeterministicRandomizer ( uint64_t id ) const
2016-06-08 18:05:01 +02:00
{
2016-09-19 17:05:35 +02:00
return CSipHasher ( nSeed0 , nSeed1 ) . Write ( id ) ;
}
2016-06-08 18:05:01 +02:00
2017-02-06 14:31:37 +01:00
uint64_t CConnman : : CalculateKeyedNetGroup ( const CAddress & ad ) const
2016-09-19 17:05:35 +02:00
{
2021-05-11 17:11:07 +02:00
std : : vector < unsigned char > vchNetGroup ( ad . GetGroup ( addrman . m_asmap ) ) ;
2016-06-08 18:05:01 +02:00
2017-07-13 01:23:59 +02:00
return GetDeterministicRandomizer ( RANDOMIZER_ID_NETGROUP ) . Write ( vchNetGroup . data ( ) , vchNetGroup . size ( ) ) . Finalize ( ) ;
2016-06-08 18:05:01 +02:00
}
2020-04-07 17:58:38 +02:00
void CConnman : : RegisterEvents ( CNode * pnode )
{
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode ! = SOCKETEVENTS_KQUEUE ) {
return ;
}
LOCK ( pnode - > cs_hSocket ) ;
assert ( pnode - > hSocket ! = INVALID_SOCKET ) ;
struct kevent events [ 2 ] ;
EV_SET ( & events [ 0 ] , pnode - > hSocket , EVFILT_READ , EV_ADD , 0 , 0 , nullptr ) ;
EV_SET ( & events [ 1 ] , pnode - > hSocket , EVFILT_WRITE , EV_ADD | EV_CLEAR , 0 , 0 , nullptr ) ;
int r = kevent ( kqueuefd , events , 2 , nullptr , 0 , nullptr ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- kevent(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
kqueuefd , EV_ADD , pnode - > hSocket , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode ! = SOCKETEVENTS_EPOLL ) {
return ;
}
LOCK ( pnode - > cs_hSocket ) ;
assert ( pnode - > hSocket ! = INVALID_SOCKET ) ;
epoll_event e ;
// We're using edge-triggered mode, so it's important that we drain sockets even if no signals come in
e . events = EPOLLIN | EPOLLOUT | EPOLLET | EPOLLERR | EPOLLHUP ;
e . data . fd = pnode - > hSocket ;
int r = epoll_ctl ( epollfd , EPOLL_CTL_ADD , pnode - > hSocket , & e ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- epoll_ctl(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
epollfd , EPOLL_CTL_ADD , pnode - > hSocket , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
}
# endif
}
void CConnman : : UnregisterEvents ( CNode * pnode )
{
2020-12-30 20:34:42 +01:00
# ifdef USE_KQUEUE
if ( socketEventsMode ! = SOCKETEVENTS_KQUEUE ) {
return ;
}
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket = = INVALID_SOCKET ) {
return ;
}
struct kevent events [ 2 ] ;
EV_SET ( & events [ 0 ] , pnode - > hSocket , EVFILT_READ , EV_DELETE , 0 , 0 , nullptr ) ;
EV_SET ( & events [ 1 ] , pnode - > hSocket , EVFILT_WRITE , EV_DELETE , 0 , 0 , nullptr ) ;
int r = kevent ( kqueuefd , events , 2 , nullptr , 0 , nullptr ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- kevent(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
kqueuefd , EV_DELETE , pnode - > hSocket , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
}
# endif
2020-04-07 17:58:38 +02:00
# ifdef USE_EPOLL
if ( socketEventsMode ! = SOCKETEVENTS_EPOLL ) {
return ;
}
LOCK ( pnode - > cs_hSocket ) ;
if ( pnode - > hSocket = = INVALID_SOCKET ) {
return ;
}
int r = epoll_ctl ( epollfd , EPOLL_CTL_DEL , pnode - > hSocket , nullptr ) ;
if ( r ! = 0 ) {
LogPrint ( BCLog : : NET , " %s -- epoll_ctl(%d, %d, %d, ...) failed. error: %s \n " , __func__ ,
epollfd , EPOLL_CTL_DEL , pnode - > hSocket , NetworkErrorString ( WSAGetLastError ( ) ) ) ;
}
# endif
}