mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 11:32:46 +01:00
Merge #6365: backport: merge bitcoin#22778, #25156, #26497, #27213, #28189, #28155, #28895, partial bitcoin#26396 (networking backports: part 9)
09504bdd1f
merge bitcoin#28895: do not make automatic outbound connections to addnode peers (Kittywhiskers Van Gogh)6cf206ca0e
merge bitcoin#28155: improves addnode / m_added_nodes logic (Kittywhiskers Van Gogh)11d654af19
merge bitcoin#28189: diversify network outbounds release note (Kittywhiskers Van Gogh)5dc52b3b6f
merge bitcoin#27213: Diversify automatic outbound connections with respect to networks (Kittywhiskers Van Gogh)291305b4d2
merge bitcoin#26497: Make ConsumeNetAddr always produce valid onion addresses (Kittywhiskers Van Gogh)6a37934af4
partial bitcoin#26396: Avoid SetTxRelay for feeler connections (Kittywhiskers Van Gogh)221a78ea84
merge bitcoin#25156: Introduce PeerManagerImpl::RejectIncomingTxs (Kittywhiskers Van Gogh)cc694c2e5b
merge bitcoin#22778: Reduce resource usage for inbound block-relay-only connections (Kittywhiskers Van Gogh)6e6de54e5e
net: use `Peer::m_can_relay_tx` when we mean `m_tx_relay != nullptr` (Kittywhiskers Van Gogh)77526d2129
net: rename `Peer::m_block_relay_only` to `Peer::m_can_tx_relay` (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Dependency for https://github.com/dashpay/dash/pull/6333 * When backporting [bitcoin#22778](https://github.com/bitcoin/bitcoin/pull/22778), the `m_tx_inventory_known_filter.insert()` call in `queueAndMaybePushInv()` had to be moved out as `EXCLUSIVE_LOCKS_REQUIRED` does not seem to work with lambda parameters list (though it does work with capture list, [source](4b5e39290c/src/net_processing.cpp (L5781)
)), see error below <details> <summary>Compiler error:</summary> ``` net_processing.cpp:5895:21: note: found near match 'tx_relay->m_tx_inventory_mutex' net_processing.cpp:5955:21: error: calling function 'operator()' requires holding mutex 'queueAndMaybePushInv.m_tx_inventory_mutex' exclusively [-Werror,-Wthread-safety-precise] queueAndMaybePushInv(tx_relay, CInv(nInvType, hash)); ^ net_processing.cpp:5955:21: note: found near match 'tx_relay->m_tx_inventory_mutex' net_processing.cpp:5977:17: error: calling function 'operator()' requires holding mutex 'queueAndMaybePushInv.m_tx_inventory_mutex' exclusively [-Werror,-Wthread-safety-precise] queueAndMaybePushInv(inv_relay, inv); ^ ``` </details> * Attempting to remove the `EXCLUSIVE_LOCKS_REQUIRED` or the `AssertLockHeld` are not options due to stricter thread sanitization checks being applied since [dash#6319](https://github.com/dashpay/dash/pull/6319) (and in general, removing annotations being inadvisable regardless) * We cannot simply lock `peer->GetInvRelay()->m_tx_inventory_mutex` as a) the caller already has a copy of the relay pointer (which means that `m_tx_relay_mutex` was already held) that b) they used to lock `m_tx_inventory_mutex` (which we were already asserting) so c) we cannot simply re-lock `m_tx_inventory_mutex` as it's already already held, just through a less circuitous path. * The reason locking is mentioned instead of asserting is that the compiler treats `peer->GetInvRelay()->m_tx_inventory_mutex` and `tx_relay->m_tx_inventory_mutex` as separate locks (despite being different ways of accessing the same thing) and would complain similarly to the error above if attempting to assert the former while holding the latter. * As `m_tx_relay` is always initialized for Dash, to mimic the behaviour _expected_ upstream, in [bitcoin#22778](https://github.com/bitcoin/bitcoin/pull/22778), `SetTxRelay()` will _allow_ `GetTxRelay()` to return `m_can_tx_relay` (while Dash code that demands unconditional access can use `GetInvRelay()` instead) with the lack of `SetTxRelay()` resulting in `GetTxRelay()` feigning the absence of `m_can_tx_relay`. This allows us to retain the same style of checks used upstream instead of using proxies like `!CNode::IsBlockOnlyConn()` to determined if we _should_ use `m_tx_relay`. * Speaking of proxies, being a block-only connection is now no longer the only reason not to relay transactions In preparation for [bitcoin#26396](https://github.com/bitcoin/bitcoin/pull/26396), proxy usage of `!CNode::IsBlockOnlyConn()` and `!Peer::m_block_relay_only` was replaced with the more explicit `Peer::m_can_tx_relay` before being used to gate the result of `GetTxRelay()`, to help it mimic upstream semantics. ## Breaking Changes None expected ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas **(note: N/A)** - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK09504bdd1f
Tree-SHA512: f4f36f12f749b697dd4ad5521ed15f862c93ed4492a047759554aa80a3ce00dbd1bdc0242f7a4468f41f25925d5b79c8ab774d8489317437b1983f0a1277eecb
This commit is contained in:
commit
25c3355053
8
doc/release-notes-6365.md
Normal file
8
doc/release-notes-6365.md
Normal file
@ -0,0 +1,8 @@
|
||||
P2P and network changes
|
||||
------
|
||||
|
||||
- Nodes with multiple reachable networks will actively try to have at least one
|
||||
outbound connection to each network. This improves individual resistance to
|
||||
eclipse attacks and network level resistance to partition attacks. Users no
|
||||
longer need to perform active measures to ensure being connected to multiple
|
||||
enabled networks.
|
@ -134,6 +134,7 @@ BITCOIN_TESTS =\
|
||||
test/minisketch_tests.cpp \
|
||||
test/miner_tests.cpp \
|
||||
test/multisig_tests.cpp \
|
||||
test/net_peer_connection_tests.cpp \
|
||||
test/net_peer_eviction_tests.cpp \
|
||||
test/net_tests.cpp \
|
||||
test/netbase_tests.cpp \
|
||||
|
@ -11,7 +11,8 @@ TEST_FUZZ_H = \
|
||||
test/fuzz/fuzz.h \
|
||||
test/fuzz/FuzzedDataProvider.h \
|
||||
test/fuzz/util.h \
|
||||
test/util/mining.h
|
||||
test/util/mining.h \
|
||||
test/fuzz/util/net.h
|
||||
|
||||
libtest_fuzz_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
|
||||
libtest_fuzz_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
|
||||
@ -19,6 +20,7 @@ libtest_fuzz_a_SOURCES = \
|
||||
test/fuzz/fuzz.cpp \
|
||||
test/util/mining.cpp \
|
||||
test/fuzz/util.cpp \
|
||||
test/fuzz/util/net.cpp \
|
||||
$(TEST_FUZZ_H)
|
||||
|
||||
LIBTEST_FUZZ += $(LIBBITCOIN_SERVER)
|
||||
|
141
src/net.cpp
141
src/net.cpp
@ -107,6 +107,9 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
|
||||
// A random time period (0 to 1 seconds) is added to feeler connections to prevent synchronization.
|
||||
static constexpr auto FEELER_SLEEP_WINDOW{1s};
|
||||
|
||||
/** Frequency to attempt extra connections to reachable networks we're not connected to yet **/
|
||||
static constexpr auto EXTRA_NETWORK_PEER_INTERVAL{5min};
|
||||
|
||||
/** Used to pass flags to the Bind() function */
|
||||
enum BindFlags {
|
||||
BF_NONE = 0,
|
||||
@ -520,21 +523,25 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||
const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
|
||||
Params().GetDefaultPort()};
|
||||
if (pszDest) {
|
||||
const std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
|
||||
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
|
||||
if (!resolved.empty()) {
|
||||
const CService& rnd{resolved[GetRand(resolved.size())]};
|
||||
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE};
|
||||
if (!addrConnect.IsValid()) {
|
||||
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
|
||||
return nullptr;
|
||||
}
|
||||
// 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.
|
||||
LOCK(m_nodes_mutex);
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode) {
|
||||
LogPrintf("Failed to open new connection, already connected\n");
|
||||
return nullptr;
|
||||
Shuffle(resolved.begin(), resolved.end(), FastRandomContext());
|
||||
// If the connection is made by name, it can be the case that the name resolves to more than one address.
|
||||
// We don't want to connect any more of them if we are already connected to one
|
||||
for (const auto& r : resolved) {
|
||||
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(r), NODE_NONE};
|
||||
if (!addrConnect.IsValid()) {
|
||||
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
|
||||
return nullptr;
|
||||
}
|
||||
// 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.
|
||||
LOCK(m_nodes_mutex);
|
||||
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
|
||||
if (pnode) {
|
||||
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2252,6 +2259,9 @@ void CConnman::DisconnectNodes()
|
||||
// close socket and cleanup
|
||||
pnode->CloseSocketDisconnect(this);
|
||||
|
||||
// update connection count by network
|
||||
if (pnode->IsManualOrFullOutboundConn()) --m_network_conn_counts[pnode->addr.GetNetwork()];
|
||||
|
||||
// hold in disconnected pool until all refs are released
|
||||
pnode->Release();
|
||||
m_nodes_disconnected.push_back(pnode);
|
||||
@ -3182,6 +3192,28 @@ std::unordered_set<Network> CConnman::GetReachableEmptyNetworks() const
|
||||
return networks;
|
||||
}
|
||||
|
||||
bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
|
||||
{
|
||||
AssertLockHeld(m_nodes_mutex);
|
||||
return m_network_conn_counts[net] > 1;
|
||||
}
|
||||
|
||||
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
|
||||
{
|
||||
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
|
||||
Shuffle(nets.begin(), nets.end(), FastRandomContext());
|
||||
|
||||
LOCK(m_nodes_mutex);
|
||||
for (const auto net : nets) {
|
||||
if (IsReachable(net) && m_network_conn_counts[net] == 0 && addrman.Size(net) != 0) {
|
||||
network = net;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDeterministicMNManager& dmnman)
|
||||
{
|
||||
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
|
||||
@ -3217,6 +3249,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
// Minimum time before next feeler connection (in microseconds).
|
||||
auto next_feeler = GetExponentialRand(start, FEELER_INTERVAL);
|
||||
auto next_extra_block_relay = GetExponentialRand(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
|
||||
auto next_extra_network_peer{GetExponentialRand(start, EXTRA_NETWORK_PEER_INTERVAL)};
|
||||
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
|
||||
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
|
||||
const bool use_seednodes{gArgs.IsArgSet("-seednode")};
|
||||
@ -3345,6 +3378,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
auto now = GetTime<std::chrono::microseconds>();
|
||||
bool anchor = false;
|
||||
bool fFeeler = false;
|
||||
std::optional<Network> preferred_net;
|
||||
bool onion_only = false;
|
||||
|
||||
// Determine what type of connection to open. Opening
|
||||
@ -3395,6 +3429,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
next_feeler = GetExponentialRand(now, FEELER_INTERVAL);
|
||||
conn_type = ConnectionType::FEELER;
|
||||
fFeeler = true;
|
||||
} else if (nOutboundFullRelay == m_max_outbound_full_relay &&
|
||||
m_max_outbound_full_relay == MAX_OUTBOUND_FULL_RELAY_CONNECTIONS &&
|
||||
now > next_extra_network_peer &&
|
||||
MaybePickPreferredNetwork(preferred_net)) {
|
||||
// Full outbound connection management: Attempt to get at least one
|
||||
// outbound peer from each reachable network by making extra connections
|
||||
// and then protecting "only" peers from a network during outbound eviction.
|
||||
// This is not attempted if the user changed -maxconnections to a value
|
||||
// so low that less than MAX_OUTBOUND_FULL_RELAY_CONNECTIONS are made,
|
||||
// to prevent interactions with otherwise protected outbound peers.
|
||||
next_extra_network_peer = GetExponentialRand(now, EXTRA_NETWORK_PEER_INTERVAL);
|
||||
} else if (nOutboundOnionRelay < m_max_outbound_onion && IsReachable(Network::NET_ONION)) {
|
||||
onion_only = true;
|
||||
} else {
|
||||
@ -3452,7 +3497,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
}
|
||||
} else {
|
||||
// Not a feeler
|
||||
std::tie(addr, addr_last_try) = addrman.Select();
|
||||
// If preferred_net has a value set, pick an extra outbound
|
||||
// peer from that network. The eviction logic in net_processing
|
||||
// ensures that a peer from another network will be evicted.
|
||||
std::tie(addr, addr_last_try) = addrman.Select(false, preferred_net);
|
||||
}
|
||||
|
||||
auto dmn = mnList.GetMNByService(addr);
|
||||
@ -3503,6 +3551,17 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do not make automatic outbound connections to addnode peers, to
|
||||
// not use our limited outbound slots for them and to ensure
|
||||
// addnode connections benefit from their intended protections.
|
||||
if (AddedNodesContain(addr)) {
|
||||
LogPrint(BCLog::NET, "Not making automatic %s%s connection to %s peer selected for manual (addnode) connection%s\n",
|
||||
preferred_net.has_value() ? "network-specific " : "",
|
||||
ConnectionTypeAsString(conn_type), GetNetworkName(addr.GetNetwork()),
|
||||
fLogIPs ? strprintf(": %s", addr.ToStringAddrPort()) : "");
|
||||
continue;
|
||||
}
|
||||
|
||||
addrConnect = addr;
|
||||
break;
|
||||
}
|
||||
@ -3519,6 +3578,9 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, CDe
|
||||
LogPrint(BCLog::NET, "Making feeler connection\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (preferred_net != std::nullopt) LogPrint(BCLog::NET, "Making network specific connection to %s on %s.\n", addrConnect.ToStringAddrPort(), GetNetworkName(preferred_net.value()));
|
||||
|
||||
// Record addrman failure attempts when node has at least 2 persistent outbound connections to peers with
|
||||
// different netgroups in ipv4/ipv6 networks + all peers in Tor/I2P/CJDNS networks.
|
||||
// Don't record addrman failure attempts when node is offline. This can be identified since all local
|
||||
@ -3544,7 +3606,7 @@ std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
|
||||
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo(bool include_connected) const
|
||||
{
|
||||
std::vector<AddedNodeInfo> ret;
|
||||
|
||||
@ -3579,6 +3641,9 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
|
||||
// strAddNode is an IP:port
|
||||
auto it = mapConnected.find(service);
|
||||
if (it != mapConnected.end()) {
|
||||
if (!include_connected) {
|
||||
continue;
|
||||
}
|
||||
addedNode.resolvedAddress = service;
|
||||
addedNode.fConnected = true;
|
||||
addedNode.fInbound = it->second;
|
||||
@ -3587,6 +3652,9 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
|
||||
// strAddNode is a name
|
||||
auto it = mapConnectedByName.find(addr.m_added_node);
|
||||
if (it != mapConnectedByName.end()) {
|
||||
if (!include_connected) {
|
||||
continue;
|
||||
}
|
||||
addedNode.resolvedAddress = it->second.second;
|
||||
addedNode.fConnected = true;
|
||||
addedNode.fInbound = it->second.first;
|
||||
@ -3605,21 +3673,19 @@ void CConnman::ThreadOpenAddedConnections()
|
||||
while (true)
|
||||
{
|
||||
CSemaphoreGrant grant(*semAddnode);
|
||||
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo();
|
||||
std::vector<AddedNodeInfo> vInfo = GetAddedNodeInfo(/*include_connected=*/false);
|
||||
bool tried = false;
|
||||
for (const AddedNodeInfo& info : vInfo) {
|
||||
if (!info.fConnected) {
|
||||
if (!grant) {
|
||||
// If we've used up our semaphore and need a new one, let's not wait here since while we are waiting
|
||||
// the addednodeinfo state might change.
|
||||
break;
|
||||
}
|
||||
tried = true;
|
||||
CAddress addr(CService(), NODE_NONE);
|
||||
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
|
||||
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
|
||||
if (!grant) {
|
||||
// If we've used up our semaphore and need a new one, let's not wait here since while we are waiting
|
||||
// the addednodeinfo state might change.
|
||||
break;
|
||||
}
|
||||
tried = true;
|
||||
CAddress addr(CService(), NODE_NONE);
|
||||
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
|
||||
if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return;
|
||||
grant = CSemaphoreGrant(*semAddnode, /*fTry=*/true);
|
||||
}
|
||||
// See if any reconnections are desired.
|
||||
PerformReconnections();
|
||||
@ -3881,6 +3947,9 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
m_nodes.push_back(pnode);
|
||||
|
||||
// update connection count by network
|
||||
if (pnode->IsManualOrFullOutboundConn()) ++m_network_conn_counts[pnode->addr.GetNetwork()];
|
||||
}
|
||||
{
|
||||
if (m_edge_trig_events) {
|
||||
@ -4502,9 +4571,12 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
|
||||
|
||||
bool CConnman::AddNode(const AddedNodeParams& add)
|
||||
{
|
||||
const CService resolved(LookupNumeric(add.m_added_node, Params().GetDefaultPort(add.m_added_node)));
|
||||
const bool resolved_is_valid{resolved.IsValid()};
|
||||
|
||||
LOCK(m_added_nodes_mutex);
|
||||
for (const auto& it : m_added_node_params) {
|
||||
if (add.m_added_node == it.m_added_node) return false;
|
||||
if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, Params().GetDefaultPort(it.m_added_node)))) return false;
|
||||
}
|
||||
|
||||
m_added_node_params.push_back(add);
|
||||
@ -4523,6 +4595,17 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CConnman::AddedNodesContain(const CAddress& addr) const
|
||||
{
|
||||
AssertLockNotHeld(m_added_nodes_mutex);
|
||||
const std::string addr_str{addr.ToStringAddr()};
|
||||
const std::string addr_port_str{addr.ToStringAddrPort()};
|
||||
LOCK(m_added_nodes_mutex);
|
||||
return (m_added_node_params.size() < 24 // bound the query to a reasonable limit
|
||||
&& std::any_of(m_added_node_params.cbegin(), m_added_node_params.cend(),
|
||||
[&](const auto& p) { return p.m_added_node == addr_str || p.m_added_node == addr_port_str; }));
|
||||
}
|
||||
|
||||
bool CConnman::AddPendingMasternode(const uint256& proTxHash)
|
||||
{
|
||||
LOCK(cs_vPendingMasternodes);
|
||||
|
45
src/net.h
45
src/net.h
@ -889,6 +889,22 @@ public:
|
||||
return m_conn_type == ConnectionType::MANUAL;
|
||||
}
|
||||
|
||||
bool IsManualOrFullOutboundConn() const
|
||||
{
|
||||
switch (m_conn_type) {
|
||||
case ConnectionType::INBOUND:
|
||||
case ConnectionType::FEELER:
|
||||
case ConnectionType::BLOCK_RELAY:
|
||||
case ConnectionType::ADDR_FETCH:
|
||||
return false;
|
||||
case ConnectionType::OUTBOUND_FULL_RELAY:
|
||||
case ConnectionType::MANUAL:
|
||||
return true;
|
||||
} // no default case, so the compiler can warn about missing cases
|
||||
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bool IsBlockOnlyConn() const {
|
||||
return m_conn_type == ConnectionType::BLOCK_RELAY;
|
||||
}
|
||||
@ -929,10 +945,8 @@ public:
|
||||
/** Whether this peer provides all services that we want. Used for eviction decisions */
|
||||
std::atomic_bool m_has_all_wanted_services{false};
|
||||
|
||||
/** Whether we should relay transactions to this peer (their version
|
||||
* message did not include fRelay=false and this is not a block-relay-only
|
||||
* connection). This only changes from false to true. It will never change
|
||||
* back to false. Used only in inbound eviction logic. */
|
||||
/** Whether we should relay transactions to this peer. This only changes
|
||||
* from false to true. It will never change back to false. */
|
||||
std::atomic_bool m_relays_txs{false};
|
||||
|
||||
/** Whether this peer has loaded a bloom filter. Used only in inbound
|
||||
@ -1283,6 +1297,9 @@ public:
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex, !mutexMsgProc);
|
||||
bool CheckIncomingNonce(uint64_t nonce);
|
||||
|
||||
// alias for thread safety annotations only, not defined
|
||||
RecursiveMutex& GetNodesMutex() const LOCK_RETURNED(m_nodes_mutex);
|
||||
|
||||
struct CFullyConnectedOnly {
|
||||
bool operator() (const CNode* pnode) const {
|
||||
return NodeFullyConnected(pnode);
|
||||
@ -1464,7 +1481,8 @@ public:
|
||||
|
||||
bool AddNode(const AddedNodeParams& add) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
||||
bool RemoveAddedNode(const std::string& node) EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
||||
std::vector<AddedNodeInfo> GetAddedNodeInfo() const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
||||
bool AddedNodesContain(const CAddress& addr) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
||||
std::vector<AddedNodeInfo> GetAddedNodeInfo(bool include_connected) const EXCLUSIVE_LOCKS_REQUIRED(!m_added_nodes_mutex);
|
||||
|
||||
/**
|
||||
* Attempts to open a connection. Currently only used from tests.
|
||||
@ -1536,6 +1554,8 @@ public:
|
||||
/** Return true if we should disconnect the peer for failing an inactivity check. */
|
||||
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
|
||||
|
||||
bool MultipleManualOrFullOutboundConns(Network net) const EXCLUSIVE_LOCKS_REQUIRED(m_nodes_mutex);
|
||||
|
||||
/**
|
||||
* RAII helper to atomically create a copy of `m_nodes` and add a reference
|
||||
* to each of the nodes. The nodes are released when this object is destroyed.
|
||||
@ -1736,6 +1756,18 @@ private:
|
||||
*/
|
||||
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
|
||||
|
||||
/**
|
||||
* Search for a "preferred" network, a reachable network to which we
|
||||
* currently don't have any OUTBOUND_FULL_RELAY or MANUAL connections.
|
||||
* There needs to be at least one address in AddrMan for a preferred
|
||||
* network to be picked.
|
||||
*
|
||||
* @param[out] network Preferred network, if found.
|
||||
*
|
||||
* @return bool Whether a preferred network was found.
|
||||
*/
|
||||
bool MaybePickPreferredNetwork(std::optional<Network>& network);
|
||||
|
||||
// Whether the node should be passed out in ForEach* callbacks
|
||||
static bool NodeFullyConnected(const CNode* pnode);
|
||||
|
||||
@ -1778,6 +1810,9 @@ private:
|
||||
std::atomic<NodeId> nLastNodeId{0};
|
||||
unsigned int nPrevNodeCount{0};
|
||||
|
||||
// Stores number of full-tx connections (outbound and manual) per network
|
||||
std::array<unsigned int, Network::NET_MAX> m_network_conn_counts GUARDED_BY(m_nodes_mutex) = {};
|
||||
|
||||
std::vector<uint256> vPendingMasternodes;
|
||||
mutable RecursiveMutex cs_vPendingMasternodes;
|
||||
std::map<std::pair<Consensus::LLMQType, uint256>, std::set<uint256>> masternodeQuorumNodes GUARDED_BY(cs_vPendingMasternodes);
|
||||
|
@ -305,10 +305,25 @@ struct Peer {
|
||||
std::chrono::microseconds m_next_inv_send_time GUARDED_BY(NetEventsInterface::g_msgproc_mutex){0};
|
||||
};
|
||||
|
||||
// in bitcoin: m_tx_relay == nullptr if we're not relaying transactions with this peer
|
||||
// in dash: m_tx_relay should never be nullptr, we don't relay transactions if
|
||||
// `IsBlockOnlyConn() == true` is instead
|
||||
std::unique_ptr<TxRelay> m_tx_relay{std::make_unique<TxRelay>()};
|
||||
/**
|
||||
* (Bitcoin) Initializes a TxRelay struct for this peer. Can be called at most once for a peer.
|
||||
* (Dash) Enables the flag that allows GetTxRelay() to return m_tx_relay */
|
||||
TxRelay* SetTxRelay()
|
||||
{
|
||||
Assume(!m_can_tx_relay);
|
||||
m_can_tx_relay = true;
|
||||
return WITH_LOCK(m_tx_relay_mutex, return m_tx_relay.get());
|
||||
};
|
||||
|
||||
TxRelay* GetInvRelay()
|
||||
{
|
||||
return WITH_LOCK(m_tx_relay_mutex, return m_tx_relay.get());
|
||||
}
|
||||
|
||||
TxRelay* GetTxRelay()
|
||||
{
|
||||
return m_can_tx_relay ? WITH_LOCK(m_tx_relay_mutex, return m_tx_relay.get()) : nullptr;
|
||||
};
|
||||
|
||||
/** A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
|
||||
std::vector<CAddress> m_addrs_to_send GUARDED_BY(NetEventsInterface::g_msgproc_mutex);
|
||||
@ -337,8 +352,8 @@ struct Peer {
|
||||
* This field must correlate with whether m_addr_known has been
|
||||
* initialized.*/
|
||||
std::atomic_bool m_addr_relay_enabled{false};
|
||||
/** Whether a Peer can only be relayed blocks */
|
||||
const bool m_block_relay_only{false};
|
||||
/** Whether a peer can relay transactions */
|
||||
bool m_can_tx_relay{false};
|
||||
/** Whether a getaddr request to this peer is outstanding. */
|
||||
bool m_getaddr_sent GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
|
||||
/** Guards address sending timers. */
|
||||
@ -376,12 +391,20 @@ struct Peer {
|
||||
/** Time of the last getheaders message to this peer */
|
||||
std::atomic<std::chrono::seconds> m_last_getheaders_timestamp GUARDED_BY(NetEventsInterface::g_msgproc_mutex){0s};
|
||||
|
||||
explicit Peer(NodeId id, ServiceFlags our_services, bool block_relay_only)
|
||||
explicit Peer(NodeId id, ServiceFlags our_services)
|
||||
: m_id(id)
|
||||
, m_our_services{our_services}
|
||||
, m_tx_relay(std::make_unique<TxRelay>())
|
||||
, m_block_relay_only{block_relay_only}
|
||||
{}
|
||||
|
||||
private:
|
||||
Mutex m_tx_relay_mutex;
|
||||
|
||||
/** Transaction relay data.
|
||||
* (Bitcoin) Transaction relay data. May be a nullptr.
|
||||
* (Dash) Always initialized but selectively available through GetTxRelay()
|
||||
* (non-transaction relay should use GetInvRelay(), which will provide
|
||||
* unconditional access) */
|
||||
std::unique_ptr<TxRelay> m_tx_relay GUARDED_BY(m_tx_relay_mutex){std::make_unique<TxRelay>()};
|
||||
};
|
||||
|
||||
using PeerRef = std::shared_ptr<Peer>;
|
||||
@ -722,9 +745,11 @@ private:
|
||||
/** Next time to check for stale tip */
|
||||
std::chrono::seconds m_stale_tip_check_time GUARDED_BY(cs_main){0s};
|
||||
|
||||
/** Whether this node is running in blocks only mode */
|
||||
/** Whether this node is running in -blocksonly mode */
|
||||
const bool m_ignore_incoming_txs;
|
||||
|
||||
bool RejectIncomingTxs(const CNode& peer) const;
|
||||
|
||||
/** Whether we've completed initial sync yet, for determining when to turn
|
||||
* on extra block-relay-only peers. */
|
||||
bool m_initial_sync_finished GUARDED_BY(cs_main){false};
|
||||
@ -1034,11 +1059,11 @@ void PeerManagerImpl::PushAddress(Peer& peer, const CAddress& addr, FastRandomCo
|
||||
|
||||
static void AddKnownInv(Peer& peer, const uint256& hash)
|
||||
{
|
||||
// Dash always initializes m_tx_relay
|
||||
assert(peer.m_tx_relay != nullptr);
|
||||
auto inv_relay = peer.GetInvRelay();
|
||||
assert(inv_relay);
|
||||
|
||||
LOCK(peer.m_tx_relay->m_tx_inventory_mutex);
|
||||
peer.m_tx_relay->m_tx_inventory_known_filter.insert(hash);
|
||||
LOCK(inv_relay->m_tx_inventory_mutex);
|
||||
inv_relay->m_tx_inventory_known_filter.insert(hash);
|
||||
}
|
||||
|
||||
/** Whether this peer can serve us blocks. */
|
||||
@ -1072,8 +1097,8 @@ static uint16_t GetHeadersLimit(const CNode& pfrom, bool compressed)
|
||||
|
||||
static void PushInv(Peer& peer, const CInv& inv)
|
||||
{
|
||||
// Dash always initializes m_tx_relay
|
||||
assert(peer.m_tx_relay != nullptr);
|
||||
auto inv_relay = peer.GetInvRelay();
|
||||
assert(inv_relay);
|
||||
|
||||
ASSERT_IF_DEBUG(inv.type != MSG_BLOCK);
|
||||
if (inv.type == MSG_BLOCK) {
|
||||
@ -1081,17 +1106,17 @@ static void PushInv(Peer& peer, const CInv& inv)
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK(peer.m_tx_relay->m_tx_inventory_mutex);
|
||||
if (peer.m_tx_relay->m_tx_inventory_known_filter.contains(inv.hash)) {
|
||||
LOCK(inv_relay->m_tx_inventory_mutex);
|
||||
if (inv_relay->m_tx_inventory_known_filter.contains(inv.hash)) {
|
||||
LogPrint(BCLog::NET, "%s -- skipping known inv: %s peer=%d\n", __func__, inv.ToString(), peer.m_id);
|
||||
return;
|
||||
}
|
||||
LogPrint(BCLog::NET, "%s -- adding new inv: %s peer=%d\n", __func__, inv.ToString(), peer.m_id);
|
||||
if (inv.type == MSG_TX || inv.type == MSG_DSTX) {
|
||||
peer.m_tx_relay->m_tx_inventory_to_send.insert(inv.hash);
|
||||
inv_relay->m_tx_inventory_to_send.insert(inv.hash);
|
||||
return;
|
||||
}
|
||||
peer.m_tx_relay->vInventoryOtherToSend.push_back(inv);
|
||||
inv_relay->vInventoryOtherToSend.push_back(inv);
|
||||
}
|
||||
|
||||
std::chrono::microseconds PeerManagerImpl::NextInvToInbounds(std::chrono::microseconds now,
|
||||
@ -1176,7 +1201,7 @@ void PeerManagerImpl::MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
// Never request high-bandwidth mode from peers if we're blocks-only. Our
|
||||
// When in -blocksonly mode, never request high-bandwidth mode from peers. Our
|
||||
// mempool will not contain the transactions necessary to reconstruct the
|
||||
// compact block.
|
||||
if (m_ignore_incoming_txs) return;
|
||||
@ -1553,7 +1578,7 @@ void PeerManagerImpl::InitializeNode(CNode& node, ServiceFlags our_services) {
|
||||
LOCK(cs_main);
|
||||
m_node_states.emplace_hint(m_node_states.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(node.IsInboundConn()));
|
||||
}
|
||||
PeerRef peer = std::make_shared<Peer>(nodeid, our_services, /* block_relay_only = */ node.IsBlockOnlyConn());
|
||||
PeerRef peer = std::make_shared<Peer>(nodeid, our_services);
|
||||
{
|
||||
LOCK(m_peer_mutex);
|
||||
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, peer);
|
||||
@ -1686,8 +1711,8 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
|
||||
ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start.load();
|
||||
}
|
||||
|
||||
if (!peer->m_block_relay_only) {
|
||||
stats.m_relay_txs = WITH_LOCK(peer->m_tx_relay->m_bloom_filter_mutex, return peer->m_tx_relay->m_relay_txs);
|
||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
stats.m_relay_txs = WITH_LOCK(tx_relay->m_bloom_filter_mutex, return tx_relay->m_relay_txs);
|
||||
} else {
|
||||
stats.m_relay_txs = false;
|
||||
}
|
||||
@ -2212,10 +2237,11 @@ bool PeerManagerImpl::IsInvInFilter(NodeId nodeid, const uint256& hash) const
|
||||
PeerRef peer = GetPeerRef(nodeid);
|
||||
if (peer == nullptr)
|
||||
return false;
|
||||
if (peer->m_block_relay_only)
|
||||
return false;
|
||||
LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
|
||||
return peer->m_tx_relay->m_tx_inventory_known_filter.contains(hash);
|
||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||
return tx_relay->m_tx_inventory_known_filter.contains(hash);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PeerManagerImpl::PushInventory(NodeId nodeid, const CInv& inv)
|
||||
@ -2244,21 +2270,23 @@ void PeerManagerImpl::RelayInvFiltered(CInv &inv, const CTransaction& relatedTx,
|
||||
{
|
||||
// TODO: Migrate to iteration through m_peer_map
|
||||
m_connman.ForEachNode([&](CNode* pnode) {
|
||||
if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || pnode->IsBlockOnlyConn()) {
|
||||
PeerRef peer = GetPeerRef(pnode->GetId());
|
||||
if (peer == nullptr) return;
|
||||
|
||||
auto tx_relay = peer->GetTxRelay();
|
||||
if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || tx_relay == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
PeerRef peer = GetPeerRef(pnode->GetId());
|
||||
if (peer == nullptr) return;
|
||||
{
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
if (!peer->m_tx_relay->m_relay_txs) {
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
if (!tx_relay->m_relay_txs) {
|
||||
return;
|
||||
}
|
||||
if (peer->m_tx_relay->m_bloom_filter && !peer->m_tx_relay->m_bloom_filter->IsRelevantAndUpdate(relatedTx)) {
|
||||
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(relatedTx)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // LOCK(tx_relay->m_bloom_filter_mutex)
|
||||
PushInv(*peer, inv);
|
||||
});
|
||||
}
|
||||
@ -2267,21 +2295,23 @@ void PeerManagerImpl::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash,
|
||||
{
|
||||
// TODO: Migrate to iteration through m_peer_map
|
||||
m_connman.ForEachNode([&](CNode* pnode) {
|
||||
if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || pnode->IsBlockOnlyConn()) {
|
||||
PeerRef peer = GetPeerRef(pnode->GetId());
|
||||
if (peer == nullptr) return;
|
||||
|
||||
auto tx_relay = peer->GetTxRelay();
|
||||
if (pnode->nVersion < minProtoVersion || !pnode->CanRelay() || tx_relay == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
PeerRef peer = GetPeerRef(pnode->GetId());
|
||||
if (peer == nullptr) return;
|
||||
{
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
if (!peer->m_tx_relay->m_relay_txs) {
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
if (!tx_relay->m_relay_txs) {
|
||||
return;
|
||||
}
|
||||
if (peer->m_tx_relay->m_bloom_filter && !peer->m_tx_relay->m_bloom_filter->contains(relatedTxHash)) {
|
||||
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->contains(relatedTxHash)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} // LOCK(tx_relay->m_bloom_filter_mutex)
|
||||
PushInv(*peer, inv);
|
||||
});
|
||||
}
|
||||
@ -2291,7 +2321,8 @@ void PeerManagerImpl::RelayTransaction(const uint256& txid)
|
||||
LOCK(m_peer_mutex);
|
||||
for(auto& it : m_peer_map) {
|
||||
Peer& peer = *it.second;
|
||||
if (!peer.m_tx_relay) continue;
|
||||
auto tx_relay = peer.GetTxRelay();
|
||||
if (!tx_relay) continue;
|
||||
|
||||
const CInv inv{m_cj_ctx->dstxman->GetDSTX(txid) ? MSG_DSTX : MSG_TX, txid};
|
||||
PushInv(peer, inv);
|
||||
@ -2425,11 +2456,11 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
||||
} else if (inv.IsMsgFilteredBlk()) {
|
||||
bool sendMerkleBlock = false;
|
||||
CMerkleBlock merkleBlock;
|
||||
if (!pfrom.IsBlockOnlyConn()) {
|
||||
LOCK(peer.m_tx_relay->m_bloom_filter_mutex);
|
||||
if (peer.m_tx_relay->m_bloom_filter) {
|
||||
if (auto tx_relay = peer.GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
if (tx_relay->m_bloom_filter) {
|
||||
sendMerkleBlock = true;
|
||||
merkleBlock = CMerkleBlock(*pblock, *peer.m_tx_relay->m_bloom_filter);
|
||||
merkleBlock = CMerkleBlock(*pblock, *tx_relay->m_bloom_filter);
|
||||
}
|
||||
}
|
||||
if (sendMerkleBlock) {
|
||||
@ -2521,14 +2552,16 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
|
||||
{
|
||||
AssertLockNotHeld(cs_main);
|
||||
|
||||
auto tx_relay = peer.GetTxRelay();
|
||||
|
||||
std::deque<CInv>::iterator it = peer.m_getdata_requests.begin();
|
||||
std::vector<CInv> vNotFound;
|
||||
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
|
||||
|
||||
const std::chrono::seconds now = GetTime<std::chrono::seconds>();
|
||||
// Get last mempool request time
|
||||
const std::chrono::seconds mempool_req = !pfrom.IsBlockOnlyConn() ? peer.m_tx_relay->m_last_mempool_req.load()
|
||||
: std::chrono::seconds::min();
|
||||
const std::chrono::seconds mempool_req = tx_relay != nullptr ? tx_relay->m_last_mempool_req.load()
|
||||
: std::chrono::seconds::min();
|
||||
|
||||
// Process as many TX items from the front of the getdata queue as
|
||||
// possible, since they're common and it's efficient to batch process
|
||||
@ -2548,7 +2581,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
|
||||
}
|
||||
++it;
|
||||
|
||||
if (peer.m_block_relay_only && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
if (tx_relay == nullptr && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
// Note that if we receive a getdata for non-block messages
|
||||
// from a block-relay-only outbound peer that violate the policy,
|
||||
// we skip such getdata messages from this peer
|
||||
@ -2589,7 +2622,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
|
||||
|
||||
for (const uint256& parent_txid : parent_ids_to_add) {
|
||||
// Relaying a transaction with a recent but unconfirmed parent.
|
||||
if (WITH_LOCK(peer.m_tx_relay->m_tx_inventory_mutex, return !peer.m_tx_relay->m_tx_inventory_known_filter.contains(parent_txid))) {
|
||||
if (WITH_LOCK(tx_relay->m_tx_inventory_mutex, return !tx_relay->m_tx_inventory_known_filter.contains(parent_txid))) {
|
||||
LOCK(cs_main);
|
||||
State(pfrom.GetId())->m_recently_announced_invs.insert(parent_txid);
|
||||
}
|
||||
@ -3487,10 +3520,18 @@ void PeerManagerImpl::ProcessMessage(
|
||||
}
|
||||
peer->m_starting_height = starting_height;
|
||||
|
||||
if (!pfrom.IsBlockOnlyConn()) {
|
||||
// We only initialize the m_tx_relay data structure if:
|
||||
// - this isn't an outbound block-relay-only connection; and
|
||||
// - this isn't an outbound feeler connection, and
|
||||
// - fRelay=true or we're offering NODE_BLOOM to this peer
|
||||
// (NODE_BLOOM means that the peer may turn on tx relay later)
|
||||
if (!pfrom.IsBlockOnlyConn() &&
|
||||
!pfrom.IsFeelerConn() &&
|
||||
(fRelay || (peer->m_our_services & NODE_BLOOM))) {
|
||||
auto* const tx_relay = peer->SetTxRelay();
|
||||
{
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
peer->m_tx_relay->m_relay_txs = fRelay; // set to true after we get the first filter* message
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
tx_relay->m_relay_txs = fRelay; // set to true after we get the first filter* message
|
||||
}
|
||||
if (fRelay) pfrom.m_relays_txs = true;
|
||||
}
|
||||
@ -3591,8 +3632,6 @@ void PeerManagerImpl::ProcessMessage(
|
||||
// At this point, the outgoing message serialization version can't change.
|
||||
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
|
||||
|
||||
bool fBlocksOnly = pfrom.IsBlockRelayOnly();
|
||||
|
||||
if (msg_type == NetMsgType::VERACK) {
|
||||
if (pfrom.fSuccessfullyConnected) {
|
||||
LogPrint(BCLog::NET, "ignoring redundant verack message from peer=%d\n", pfrom.GetId());
|
||||
@ -3628,7 +3667,7 @@ void PeerManagerImpl::ProcessMessage(
|
||||
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, /*high_bandwidth=*/false, /*version=*/CMPCTBLOCKS_VERSION));
|
||||
}
|
||||
|
||||
if (!fBlocksOnly) {
|
||||
if (!RejectIncomingTxs(pfrom)) {
|
||||
// Tell our peer that he should send us CoinJoin queue messages
|
||||
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDDSQUEUE, true));
|
||||
// Tell our peer that he should send us intra-quorum messages
|
||||
@ -3707,7 +3746,7 @@ void PeerManagerImpl::ProcessMessage(
|
||||
}
|
||||
|
||||
// Stop processing non-block data early in blocks only mode and for block-relay-only peers
|
||||
if (fBlocksOnly && NetMessageViolatesBlocksOnly(msg_type)) {
|
||||
if (RejectIncomingTxs(pfrom) && NetMessageViolatesBlocksOnly(msg_type)) {
|
||||
LogPrint(BCLog::NET, "%s sent in violation of protocol peer=%d\n", msg_type, pfrom.GetId());
|
||||
pfrom.fDisconnect = true;
|
||||
return;
|
||||
@ -3834,6 +3873,8 @@ void PeerManagerImpl::ProcessMessage(
|
||||
return;
|
||||
}
|
||||
|
||||
const bool reject_tx_invs{RejectIncomingTxs(pfrom)};
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
const auto current_time = GetTime<std::chrono::microseconds>();
|
||||
@ -3863,7 +3904,7 @@ void PeerManagerImpl::ProcessMessage(
|
||||
best_block = &inv.hash;
|
||||
}
|
||||
} else {
|
||||
if (fBlocksOnly && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
if (reject_tx_invs && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
LogPrint(BCLog::NET, "%s (%s) inv sent in violation of protocol, disconnecting peer=%d\n", inv.GetCommand(), inv.hash.ToString(), pfrom.GetId());
|
||||
pfrom.fDisconnect = true;
|
||||
return;
|
||||
@ -3879,7 +3920,7 @@ void PeerManagerImpl::ProcessMessage(
|
||||
|
||||
AddKnownInv(*peer, inv.hash);
|
||||
if (!fAlreadyHave) {
|
||||
if (fBlocksOnly && inv.type == MSG_ISDLOCK) {
|
||||
if (reject_tx_invs && inv.type == MSG_ISDLOCK) {
|
||||
if (pfrom.GetCommonVersion() <= ADDRV2_PROTO_VERSION) {
|
||||
// It's ok to receive these invs, we just ignore them
|
||||
// and do not request corresponding objects.
|
||||
@ -4751,9 +4792,9 @@ void PeerManagerImpl::ProcessMessage(
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pfrom.IsBlockOnlyConn()) {
|
||||
LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
|
||||
peer->m_tx_relay->m_send_mempool = true;
|
||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||
tx_relay->m_send_mempool = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -4844,13 +4885,11 @@ void PeerManagerImpl::ProcessMessage(
|
||||
{
|
||||
// There is no excuse for sending a too-large filter
|
||||
Misbehaving(pfrom.GetId(), 100, "too-large bloom filter");
|
||||
}
|
||||
else if (!pfrom.IsBlockOnlyConn())
|
||||
{
|
||||
} else if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
{
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
peer->m_tx_relay->m_bloom_filter.reset(new CBloomFilter(filter));
|
||||
peer->m_tx_relay->m_relay_txs = true;
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
tx_relay->m_bloom_filter.reset(new CBloomFilter(filter));
|
||||
tx_relay->m_relay_txs = true;
|
||||
}
|
||||
pfrom.m_bloom_filter_loaded = true;
|
||||
pfrom.m_relays_txs = true;
|
||||
@ -4872,10 +4911,10 @@ void PeerManagerImpl::ProcessMessage(
|
||||
bool bad = false;
|
||||
if (vData.size() > MAX_SCRIPT_ELEMENT_SIZE) {
|
||||
bad = true;
|
||||
} else if (!pfrom.IsBlockOnlyConn()) {
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
if (peer->m_tx_relay->m_bloom_filter) {
|
||||
peer->m_tx_relay->m_bloom_filter->insert(vData);
|
||||
} else if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
if (tx_relay->m_bloom_filter) {
|
||||
tx_relay->m_bloom_filter->insert(vData);
|
||||
} else {
|
||||
bad = true;
|
||||
}
|
||||
@ -4892,14 +4931,13 @@ void PeerManagerImpl::ProcessMessage(
|
||||
pfrom.fDisconnect = true;
|
||||
return;
|
||||
}
|
||||
if (pfrom.IsBlockOnlyConn()) {
|
||||
return;
|
||||
}
|
||||
auto tx_relay = peer->GetTxRelay();
|
||||
if (!tx_relay) return;
|
||||
|
||||
{
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
peer->m_tx_relay->m_bloom_filter = nullptr;
|
||||
peer->m_tx_relay->m_relay_txs = true;
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
tx_relay->m_bloom_filter = nullptr;
|
||||
tx_relay->m_relay_txs = true;
|
||||
}
|
||||
pfrom.m_bloom_filter_loaded = false;
|
||||
pfrom.m_relays_txs = true;
|
||||
@ -5274,13 +5312,16 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now)
|
||||
// Pick the outbound-full-relay peer that least recently announced
|
||||
// us a new block, with ties broken by choosing the more recent
|
||||
// connection (higher node id)
|
||||
// Protect peers from eviction if we don't have another connection
|
||||
// to their network, counting both outbound-full-relay and manual peers.
|
||||
NodeId worst_peer = -1;
|
||||
int64_t oldest_block_announcement = std::numeric_limits<int64_t>::max();
|
||||
|
||||
// We want to prevent recently connected to Onion peers from being disconnected here, protect them as long as
|
||||
// there are more non_onion nodes than onion nodes so far
|
||||
size_t onion_count = 0;
|
||||
m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
|
||||
|
||||
m_connman.ForEachNode([&](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_connman.GetNodesMutex()) {
|
||||
AssertLockHeld(::cs_main);
|
||||
if (pnode->addr.IsTor() && ++onion_count <= m_connman.GetMaxOutboundOnionNodeCount()) return;
|
||||
// Don't disconnect masternodes just because they were slow in block announcement
|
||||
@ -5292,6 +5333,9 @@ void PeerManagerImpl::EvictExtraOutboundPeers(std::chrono::seconds now)
|
||||
if (state == nullptr) return; // shouldn't be possible, but just in case
|
||||
// Don't evict our protected peers
|
||||
if (state->m_chain_sync.m_protect) return;
|
||||
// If this is the only connection on a particular network that is
|
||||
// OUTBOUND_FULL_RELAY or MANUAL, protect it.
|
||||
if (!m_connman.MultipleManualOrFullOutboundConns(pnode->addr.GetNetwork())) return;
|
||||
if (state->m_last_block_announcement < oldest_block_announcement || (state->m_last_block_announcement == oldest_block_announcement && pnode->GetId() > worst_peer)) {
|
||||
worst_peer = pnode->GetId();
|
||||
oldest_block_announcement = state->m_last_block_announcement;
|
||||
@ -5479,6 +5523,15 @@ public:
|
||||
return mp->CompareDepthAndScore(*b, *a);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
bool PeerManagerImpl::RejectIncomingTxs(const CNode& peer) const
|
||||
{
|
||||
// block-relay-only peers may never send txs to us
|
||||
if (peer.IsBlockOnlyConn()) return true;
|
||||
// In -blocksonly mode, peers need the 'relay' permission to send txs to us
|
||||
if (m_ignore_incoming_txs && !peer.HasPermission(NetPermissionFlags::Relay)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer)
|
||||
@ -5760,9 +5813,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
LOCK(peer->m_block_inv_mutex);
|
||||
|
||||
size_t reserve = INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize() / 1000000;
|
||||
if (!pto->IsBlockOnlyConn()) {
|
||||
LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
|
||||
reserve = std::min<size_t>(peer->m_tx_relay->m_tx_inventory_to_send.size(), reserve);
|
||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||
reserve = std::min<size_t>(tx_relay->m_tx_inventory_to_send.size(), reserve);
|
||||
}
|
||||
reserve = std::max<size_t>(reserve, peer->m_blocks_for_inv_relay.size());
|
||||
reserve = std::min<size_t>(reserve, MAX_INV_SZ);
|
||||
@ -5779,9 +5832,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
peer->m_blocks_for_inv_relay.clear();
|
||||
}
|
||||
|
||||
auto queueAndMaybePushInv = [this, pto, peer, &vInv, &msgMaker](const CInv& invIn) EXCLUSIVE_LOCKS_REQUIRED(peer->m_tx_relay->m_tx_inventory_mutex) {
|
||||
AssertLockHeld(peer->m_tx_relay->m_tx_inventory_mutex);
|
||||
peer->m_tx_relay->m_tx_inventory_known_filter.insert(invIn.hash);
|
||||
auto queueAndMaybePushInv = [this, pto, peer, &vInv, &msgMaker](const CInv& invIn) {
|
||||
LogPrint(BCLog::NET, "SendMessages -- queued inv: %s index=%d peer=%d\n", invIn.ToString(), vInv.size(), pto->GetId());
|
||||
// Responses to MEMPOOL requests bypass the m_recently_announced_invs filter.
|
||||
vInv.push_back(invIn);
|
||||
@ -5792,19 +5843,19 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
}
|
||||
};
|
||||
|
||||
if (!pto->IsBlockOnlyConn()) {
|
||||
LOCK(peer->m_tx_relay->m_tx_inventory_mutex);
|
||||
if (auto tx_relay = peer->GetTxRelay(); tx_relay != nullptr) {
|
||||
LOCK(tx_relay->m_tx_inventory_mutex);
|
||||
// Check whether periodic sends should happen
|
||||
// Note: If this node is running in a Masternode mode, it makes no sense to delay outgoing txes
|
||||
// because we never produce any txes ourselves i.e. no privacy is lost in this case.
|
||||
bool fSendTrickle = pto->HasPermission(NetPermissionFlags::NoBan) || is_masternode;
|
||||
if (peer->m_tx_relay->m_next_inv_send_time < current_time) {
|
||||
if (tx_relay->m_next_inv_send_time < current_time) {
|
||||
fSendTrickle = true;
|
||||
if (pto->IsInboundConn()) {
|
||||
peer->m_tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
tx_relay->m_next_inv_send_time = NextInvToInbounds(current_time, INBOUND_INVENTORY_BROADCAST_INTERVAL);
|
||||
} else {
|
||||
// Use half the delay for Masternode outbound peers, as there is less privacy concern for them.
|
||||
peer->m_tx_relay->m_next_inv_send_time = pto->GetVerifiedProRegTxHash().IsNull() ?
|
||||
tx_relay->m_next_inv_send_time = pto->GetVerifiedProRegTxHash().IsNull() ?
|
||||
GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL) :
|
||||
GetExponentialRand(current_time, OUTBOUND_INVENTORY_BROADCAST_INTERVAL / 2);
|
||||
}
|
||||
@ -5812,49 +5863,53 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
|
||||
// Time to send but the peer has requested we not relay transactions.
|
||||
if (fSendTrickle) {
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
if (!peer->m_tx_relay->m_relay_txs) peer->m_tx_relay->m_tx_inventory_to_send.clear();
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
if (!tx_relay->m_relay_txs) tx_relay->m_tx_inventory_to_send.clear();
|
||||
}
|
||||
|
||||
// Respond to BIP35 mempool requests
|
||||
if (fSendTrickle && peer->m_tx_relay->m_send_mempool) {
|
||||
if (fSendTrickle && tx_relay->m_send_mempool) {
|
||||
auto vtxinfo = m_mempool.infoAll();
|
||||
peer->m_tx_relay->m_send_mempool = false;
|
||||
tx_relay->m_send_mempool = false;
|
||||
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
|
||||
// Send invs for txes and corresponding IS-locks
|
||||
for (const auto& txinfo : vtxinfo) {
|
||||
const uint256& hash = txinfo.tx->GetHash();
|
||||
peer->m_tx_relay->m_tx_inventory_to_send.erase(hash);
|
||||
if (peer->m_tx_relay->m_bloom_filter && !peer->m_tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
tx_relay->m_tx_inventory_to_send.erase(hash);
|
||||
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
|
||||
int nInvType = m_cj_ctx->dstxman->GetDSTX(hash) ? MSG_DSTX : MSG_TX;
|
||||
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
||||
queueAndMaybePushInv(CInv(nInvType, hash));
|
||||
|
||||
const auto islock = m_llmq_ctx->isman->GetInstantSendLockByTxid(hash);
|
||||
if (islock == nullptr) continue;
|
||||
if (pto->nVersion < ISDLOCK_PROTO_VERSION) continue;
|
||||
queueAndMaybePushInv(CInv(MSG_ISDLOCK, ::SerializeHash(*islock)));
|
||||
uint256 isLockHash{::SerializeHash(*islock)};
|
||||
tx_relay->m_tx_inventory_known_filter.insert(isLockHash);
|
||||
queueAndMaybePushInv(CInv(MSG_ISDLOCK, isLockHash));
|
||||
}
|
||||
|
||||
// Send an inv for the best ChainLock we have
|
||||
const auto& clsig = m_llmq_ctx->clhandler->GetBestChainLock();
|
||||
if (!clsig.IsNull()) {
|
||||
uint256 chainlockHash = ::SerializeHash(clsig);
|
||||
uint256 chainlockHash{::SerializeHash(clsig)};
|
||||
tx_relay->m_tx_inventory_known_filter.insert(chainlockHash);
|
||||
queueAndMaybePushInv(CInv(MSG_CLSIG, chainlockHash));
|
||||
}
|
||||
peer->m_tx_relay->m_last_mempool_req = std::chrono::duration_cast<std::chrono::seconds>(current_time);
|
||||
tx_relay->m_last_mempool_req = std::chrono::duration_cast<std::chrono::seconds>(current_time);
|
||||
}
|
||||
|
||||
// Determine transactions to relay
|
||||
if (fSendTrickle) {
|
||||
LOCK(peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
LOCK(tx_relay->m_bloom_filter_mutex);
|
||||
|
||||
// Produce a vector with all candidates for sending
|
||||
std::vector<std::set<uint256>::iterator> vInvTx;
|
||||
vInvTx.reserve(peer->m_tx_relay->m_tx_inventory_to_send.size());
|
||||
for (std::set<uint256>::iterator it = peer->m_tx_relay->m_tx_inventory_to_send.begin(); it != peer->m_tx_relay->m_tx_inventory_to_send.end(); it++) {
|
||||
vInvTx.reserve(tx_relay->m_tx_inventory_to_send.size());
|
||||
for (std::set<uint256>::iterator it = tx_relay->m_tx_inventory_to_send.begin(); it != tx_relay->m_tx_inventory_to_send.end(); it++) {
|
||||
vInvTx.push_back(it);
|
||||
}
|
||||
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
|
||||
@ -5864,7 +5919,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
// No reason to drain out at many times the network's capacity,
|
||||
// especially since we have many peers and some will draw much shorter delays.
|
||||
unsigned int nRelayedTransactions = 0;
|
||||
size_t broadcast_max{INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize() / 1000000 + (peer->m_tx_relay->m_tx_inventory_to_send.size()/1000)*5};
|
||||
size_t broadcast_max{INVENTORY_BROADCAST_MAX_PER_1MB_BLOCK * MaxBlockSize() / 1000000 + (tx_relay->m_tx_inventory_to_send.size()/1000)*5};
|
||||
broadcast_max = std::min<size_t>(1000, broadcast_max);
|
||||
|
||||
while (!vInvTx.empty() && nRelayedTransactions < broadcast_max) {
|
||||
@ -5874,9 +5929,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
vInvTx.pop_back();
|
||||
uint256 hash = *it;
|
||||
// Remove it from the to-be-sent set
|
||||
peer->m_tx_relay->m_tx_inventory_to_send.erase(it);
|
||||
tx_relay->m_tx_inventory_to_send.erase(it);
|
||||
// Check if not in the filter already
|
||||
if (peer->m_tx_relay->m_tx_inventory_known_filter.contains(hash)) {
|
||||
if (tx_relay->m_tx_inventory_known_filter.contains(hash)) {
|
||||
continue;
|
||||
}
|
||||
// Not in the mempool anymore? don't bother sending it.
|
||||
@ -5884,7 +5939,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
if (!txinfo.tx) {
|
||||
continue;
|
||||
}
|
||||
if (peer->m_tx_relay->m_bloom_filter && !peer->m_tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
if (tx_relay->m_bloom_filter && !tx_relay->m_bloom_filter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
||||
// Send
|
||||
State(pto->GetId())->m_recently_announced_invs.insert(hash);
|
||||
nRelayedTransactions++;
|
||||
@ -5902,29 +5957,33 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||
}
|
||||
}
|
||||
int nInvType = m_cj_ctx->dstxman->GetDSTX(hash) ? MSG_DSTX : MSG_TX;
|
||||
tx_relay->m_tx_inventory_known_filter.insert(hash);
|
||||
queueAndMaybePushInv(CInv(nInvType, hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
auto inv_relay = peer->GetInvRelay();
|
||||
|
||||
// Send non-tx/non-block inventory items
|
||||
LOCK2(peer->m_tx_relay->m_tx_inventory_mutex, peer->m_tx_relay->m_bloom_filter_mutex);
|
||||
LOCK2(inv_relay->m_tx_inventory_mutex, inv_relay->m_bloom_filter_mutex);
|
||||
|
||||
bool fSendIS = peer->m_tx_relay->m_relay_txs && !pto->IsBlockRelayOnly();
|
||||
bool fSendIS = inv_relay->m_relay_txs && !pto->IsBlockRelayOnly();
|
||||
|
||||
for (const auto& inv : peer->m_tx_relay->vInventoryOtherToSend) {
|
||||
if (!peer->m_tx_relay->m_relay_txs && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
for (const auto& inv : inv_relay->vInventoryOtherToSend) {
|
||||
if (!inv_relay->m_relay_txs && NetMessageViolatesBlocksOnly(inv.GetCommand())) {
|
||||
continue;
|
||||
}
|
||||
if (peer->m_tx_relay->m_tx_inventory_known_filter.contains(inv.hash)) {
|
||||
if (inv_relay->m_tx_inventory_known_filter.contains(inv.hash)) {
|
||||
continue;
|
||||
}
|
||||
if (!fSendIS && inv.type == MSG_ISDLOCK) {
|
||||
continue;
|
||||
}
|
||||
inv_relay->m_tx_inventory_known_filter.insert(inv.hash);
|
||||
queueAndMaybePushInv(inv);
|
||||
}
|
||||
peer->m_tx_relay->vInventoryOtherToSend.clear();
|
||||
inv_relay->vInventoryOtherToSend.clear();
|
||||
}
|
||||
if (!vInv.empty())
|
||||
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));
|
||||
|
@ -598,7 +598,7 @@ static std::string IPv6ToString(Span<const uint8_t> a, uint32_t scope_id)
|
||||
return r;
|
||||
}
|
||||
|
||||
static std::string OnionToString(Span<const uint8_t> addr)
|
||||
std::string OnionToString(Span<const uint8_t> addr)
|
||||
{
|
||||
uint8_t checksum[torv3::CHECKSUM_LEN];
|
||||
torv3::Checksum(addr, checksum);
|
||||
|
@ -113,6 +113,8 @@ static constexpr size_t ADDR_INTERNAL_SIZE = 10;
|
||||
/// SAM 3.1 and earlier do not support specifying ports and force the port to 0.
|
||||
static constexpr uint16_t I2P_SAM31_PORT{0};
|
||||
|
||||
std::string OnionToString(Span<const uint8_t> addr);
|
||||
|
||||
/**
|
||||
* Network address.
|
||||
*/
|
||||
|
@ -498,7 +498,7 @@ static RPCHelpMan getaddednodeinfo()
|
||||
const NodeContext& node = EnsureAnyNodeContext(request.context);
|
||||
const CConnman& connman = EnsureConnman(node);;
|
||||
|
||||
std::vector<AddedNodeInfo> vInfo = connman.GetAddedNodeInfo();
|
||||
std::vector<AddedNodeInfo> vInfo = connman.GetAddedNodeInfo(/*include_connected=*/true);
|
||||
|
||||
if (!request.params[0].isNull()) {
|
||||
bool found = false;
|
||||
|
@ -111,9 +111,19 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
|
||||
peerman.FinalizeNode(dummyNode1);
|
||||
}
|
||||
|
||||
static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman, ConnectionType connType)
|
||||
static void AddRandomOutboundPeer(NodeId& id, std::vector<CNode*>& vNodes, PeerManager& peerLogic, ConnmanTestMsg& connman, ConnectionType connType, bool onion_peer = false)
|
||||
{
|
||||
CAddress addr(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
|
||||
CAddress addr;
|
||||
|
||||
if (onion_peer) {
|
||||
auto tor_addr{g_insecure_rand_ctx.randbytes(ADDR_TORV3_SIZE)};
|
||||
BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr)));
|
||||
}
|
||||
|
||||
while (!addr.IsRoutable()) {
|
||||
addr = CAddress(ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE);
|
||||
}
|
||||
|
||||
vNodes.emplace_back(new CNode{id++,
|
||||
/*sock=*/nullptr,
|
||||
addr,
|
||||
@ -205,6 +215,30 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
|
||||
BOOST_CHECK(vNodes[max_outbound_full_relay-1]->fDisconnect == true);
|
||||
BOOST_CHECK(vNodes.back()->fDisconnect == false);
|
||||
|
||||
vNodes[max_outbound_full_relay - 1]->fDisconnect = false;
|
||||
|
||||
// Add an onion peer, that will be protected because it is the only one for
|
||||
// its network, so another peer gets disconnected instead.
|
||||
SetMockTime(time_init);
|
||||
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY, /*onion_peer=*/true);
|
||||
SetMockTime(time_later);
|
||||
peerLogic->CheckForStaleTipAndEvictPeers();
|
||||
|
||||
for (int i = 0; i < max_outbound_full_relay - 2; ++i) {
|
||||
BOOST_CHECK(vNodes[i]->fDisconnect == false);
|
||||
}
|
||||
BOOST_CHECK(vNodes[max_outbound_full_relay - 2]->fDisconnect == false);
|
||||
BOOST_CHECK(vNodes[max_outbound_full_relay - 1]->fDisconnect == true);
|
||||
BOOST_CHECK(vNodes[max_outbound_full_relay]->fDisconnect == false);
|
||||
|
||||
// Add a second onion peer which won't be protected
|
||||
SetMockTime(time_init);
|
||||
AddRandomOutboundPeer(id, vNodes, *peerLogic, *connman, ConnectionType::OUTBOUND_FULL_RELAY, /*onion_peer=*/true);
|
||||
SetMockTime(time_later);
|
||||
peerLogic->CheckForStaleTipAndEvictPeers();
|
||||
|
||||
BOOST_CHECK(vNodes.back()->fDisconnect == true);
|
||||
|
||||
for (const CNode *node : vNodes) {
|
||||
peerLogic->FinalizeNode(*node);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <time.h>
|
||||
#include <util/asmap.h>
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
#include <util/readwritefile.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/system.h>
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
@ -123,7 +124,7 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
|
||||
connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
|
||||
});
|
||||
}
|
||||
(void)connman.GetAddedNodeInfo();
|
||||
(void)connman.GetAddedNodeInfo(fuzzed_data_provider.ConsumeBool());
|
||||
(void)connman.GetExtraFullOutboundCount();
|
||||
(void)connman.GetLocalServices();
|
||||
assert(connman.GetMaxOutboundTarget() == max_outbound_limit);
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <netaddress.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <netbase.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
@ -3,6 +3,7 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <net_processing.h>
|
||||
#include <netaddress.h>
|
||||
#include <netmessagemaker.h>
|
||||
#include <test/fuzz/util.h>
|
||||
#include <test/util/script.h>
|
||||
@ -389,28 +390,6 @@ bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) n
|
||||
return false;
|
||||
}
|
||||
|
||||
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||
{
|
||||
const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
|
||||
CNetAddr net_addr;
|
||||
if (network == Network::NET_IPV4) {
|
||||
in_addr v4_addr = {};
|
||||
v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
|
||||
net_addr = CNetAddr{v4_addr};
|
||||
} else if (network == Network::NET_IPV6) {
|
||||
if (fuzzed_data_provider.remaining_bytes() >= 16) {
|
||||
in6_addr v6_addr = {};
|
||||
memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
|
||||
net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
}
|
||||
} else if (network == Network::NET_INTERNAL) {
|
||||
net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
|
||||
} else if (network == Network::NET_ONION) {
|
||||
net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
|
||||
}
|
||||
return net_addr;
|
||||
}
|
||||
|
||||
FILE* FuzzedFileProvider::open()
|
||||
{
|
||||
SetFuzzedErrNo(m_fuzzed_data_provider);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <streams.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <test/fuzz/fuzz.h>
|
||||
#include <test/fuzz/util/net.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <txmempool.h>
|
||||
@ -296,8 +297,6 @@ template<typename B = uint8_t>
|
||||
return random_bytes;
|
||||
}
|
||||
|
||||
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept;
|
||||
|
||||
inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||
{
|
||||
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
|
||||
|
36
src/test/fuzz/util/net.cpp
Normal file
36
src/test/fuzz/util/net.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <compat.h>
|
||||
#include <netaddress.h>
|
||||
#include <test/fuzz/FuzzedDataProvider.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||
{
|
||||
const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
|
||||
CNetAddr net_addr;
|
||||
if (network == Network::NET_IPV4) {
|
||||
in_addr v4_addr = {};
|
||||
v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
|
||||
net_addr = CNetAddr{v4_addr};
|
||||
} else if (network == Network::NET_IPV6) {
|
||||
if (fuzzed_data_provider.remaining_bytes() >= 16) {
|
||||
in6_addr v6_addr = {};
|
||||
memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
|
||||
net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||
}
|
||||
} else if (network == Network::NET_INTERNAL) {
|
||||
net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
|
||||
} else if (network == Network::NET_ONION) {
|
||||
auto pub_key{fuzzed_data_provider.ConsumeBytes<uint8_t>(ADDR_TORV3_SIZE)};
|
||||
pub_key.resize(ADDR_TORV3_SIZE);
|
||||
const bool ok{net_addr.SetSpecial(OnionToString(pub_key))};
|
||||
assert(ok);
|
||||
}
|
||||
return net_addr;
|
||||
}
|
14
src/test/fuzz/util/net.h
Normal file
14
src/test/fuzz/util/net.h
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_TEST_FUZZ_UTIL_NET_H
|
||||
#define BITCOIN_TEST_FUZZ_UTIL_NET_H
|
||||
|
||||
#include <netaddress.h>
|
||||
|
||||
class FuzzedDataProvider;
|
||||
|
||||
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept;
|
||||
|
||||
#endif // BITCOIN_TEST_FUZZ_UTIL_NET_H
|
156
src/test/net_peer_connection_tests.cpp
Normal file
156
src/test/net_peer_connection_tests.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright (c) 2023-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <compat.h>
|
||||
#include <net.h>
|
||||
#include <net_processing.h>
|
||||
#include <netaddress.h>
|
||||
#include <netbase.h>
|
||||
#include <netgroup.h>
|
||||
#include <node/connection_types.h>
|
||||
#include <protocol.h>
|
||||
#include <random.h>
|
||||
#include <test/util/logging.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <tinyformat.h>
|
||||
#include <version.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
struct LogIPsTestingSetup : public TestingSetup {
|
||||
LogIPsTestingSetup()
|
||||
: TestingSetup{CBaseChainParams::MAIN, /*extra_args=*/{"-logips"}} {}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(net_peer_connection_tests, LogIPsTestingSetup)
|
||||
|
||||
static CService ip(uint32_t i)
|
||||
{
|
||||
struct in_addr s;
|
||||
s.s_addr = i;
|
||||
return CService{CNetAddr{s}, Params().GetDefaultPort()};
|
||||
}
|
||||
|
||||
/** Create a peer and connect to it. If the optional `address` (IP/CJDNS only) isn't passed, a random address is created. */
|
||||
static void AddPeer(NodeId& id, std::vector<CNode*>& nodes, PeerManager& peerman, ConnmanTestMsg& connman, ConnectionType conn_type, bool onion_peer = false, std::optional<std::string> address = std::nullopt)
|
||||
{
|
||||
CAddress addr{};
|
||||
|
||||
if (address.has_value()) {
|
||||
addr = CAddress{MaybeFlipIPv6toCJDNS(LookupNumeric(address.value(), Params().GetDefaultPort())), NODE_NONE};
|
||||
} else if (onion_peer) {
|
||||
auto tor_addr{g_insecure_rand_ctx.randbytes(ADDR_TORV3_SIZE)};
|
||||
BOOST_REQUIRE(addr.SetSpecial(OnionToString(tor_addr)));
|
||||
}
|
||||
|
||||
while (!addr.IsLocal() && !addr.IsRoutable()) {
|
||||
addr = CAddress{ip(g_insecure_rand_ctx.randbits(32)), NODE_NONE};
|
||||
}
|
||||
|
||||
BOOST_REQUIRE(addr.IsValid());
|
||||
|
||||
const bool inbound_onion{onion_peer && conn_type == ConnectionType::INBOUND};
|
||||
|
||||
nodes.emplace_back(new CNode{++id,
|
||||
/*sock=*/nullptr,
|
||||
addr,
|
||||
/*nKeyedNetGroupIn=*/0,
|
||||
/*nLocalHostNonceIn=*/0,
|
||||
CAddress{},
|
||||
/*addrNameIn=*/"",
|
||||
conn_type,
|
||||
/*inbound_onion=*/inbound_onion});
|
||||
CNode& node = *nodes.back();
|
||||
node.SetCommonVersion(PROTOCOL_VERSION);
|
||||
|
||||
peerman.InitializeNode(node, ServiceFlags(NODE_NETWORK));
|
||||
node.fSuccessfullyConnected = true;
|
||||
|
||||
connman.AddTestNode(node);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection)
|
||||
{
|
||||
const auto& chainparams = Params();
|
||||
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman);
|
||||
auto peerman = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
|
||||
*m_node.chainman, *m_node.mempool, *m_node.mn_metaman, *m_node.mn_sync,
|
||||
*m_node.govman, *m_node.sporkman, /* mn_activeman = */ nullptr, m_node.dmnman,
|
||||
m_node.cj_ctx, m_node.llmq_ctx, /* ignore_incoming_txs = */ false);
|
||||
NodeId id{0};
|
||||
std::vector<CNode*> nodes;
|
||||
|
||||
// Connect a localhost peer.
|
||||
{
|
||||
ASSERT_DEBUG_LOG("Added connection to 127.0.0.1:9999 peer=1");
|
||||
AddPeer(id, nodes, *peerman, *connman, ConnectionType::MANUAL, /*onion_peer=*/false, /*address=*/"127.0.0.1");
|
||||
BOOST_REQUIRE(nodes.back() != nullptr);
|
||||
}
|
||||
|
||||
// Call ConnectNode(), which is also called by RPC addnode onetry, for a localhost
|
||||
// address that resolves to multiple IPs, including that of the connected peer.
|
||||
// The connection attempt should consistently fail due to the check in ConnectNode().
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_DEBUG_LOG("Not opening a connection to localhost, already connected to 127.0.0.1:9999");
|
||||
BOOST_CHECK(!connman->ConnectNodePublic(*peerman, "localhost", ConnectionType::MANUAL));
|
||||
}
|
||||
|
||||
// Add 3 more peer connections.
|
||||
AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
|
||||
AddPeer(id, nodes, *peerman, *connman, ConnectionType::BLOCK_RELAY, /*onion_peer=*/true);
|
||||
AddPeer(id, nodes, *peerman, *connman, ConnectionType::INBOUND);
|
||||
|
||||
BOOST_TEST_MESSAGE("Call AddNode() for all the peers");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AddNode({/*m_added_node=*/node->addr.ToStringAddrPort(), /*m_use_v2transport=*/true}));
|
||||
BOOST_TEST_MESSAGE(strprintf("peer id=%s addr=%s", node->GetId(), node->addr.ToStringAddrPort()));
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCall AddNode() with 2 addrs resolving to existing localhost addnode entry; neither should be added");
|
||||
BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.0.0.1", /*m_use_v2transport=*/true}));
|
||||
BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.1", /*m_use_v2transport=*/true}));
|
||||
|
||||
BOOST_TEST_MESSAGE("\nExpect GetAddedNodeInfo to return expected number of peers with `include_connected` true/false");
|
||||
BOOST_CHECK_EQUAL(connman->GetAddedNodeInfo(/*include_connected=*/true).size(), nodes.size());
|
||||
BOOST_CHECK(connman->GetAddedNodeInfo(/*include_connected=*/false).empty());
|
||||
|
||||
// Test AddedNodesContain()
|
||||
for (auto node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AddedNodesContain(node->addr));
|
||||
}
|
||||
AddPeer(id, nodes, *peerman, *connman, ConnectionType::OUTBOUND_FULL_RELAY);
|
||||
BOOST_CHECK(!connman->AddedNodesContain(nodes.back()->addr));
|
||||
|
||||
BOOST_TEST_MESSAGE("\nPrint GetAddedNodeInfo contents:");
|
||||
for (const auto& info : connman->GetAddedNodeInfo(/*include_connected=*/true)) {
|
||||
BOOST_TEST_MESSAGE(strprintf("\nadded node: %s", info.m_params.m_added_node));
|
||||
BOOST_TEST_MESSAGE(strprintf("connected: %s", info.fConnected));
|
||||
if (info.fConnected) {
|
||||
BOOST_TEST_MESSAGE(strprintf("IP address: %s", info.resolvedAddress.ToStringAddrPort()));
|
||||
BOOST_TEST_MESSAGE(strprintf("direction: %s", info.fInbound ? "inbound" : "outbound"));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
|
||||
for (auto node : connman->TestNodes()) {
|
||||
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr));
|
||||
}
|
||||
|
||||
// Clean up
|
||||
for (auto node : connman->TestNodes()) {
|
||||
peerman->FinalizeNode(*node);
|
||||
}
|
||||
connman->ClearTestNodes();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -4,11 +4,15 @@
|
||||
|
||||
#include <test/util/net.h>
|
||||
|
||||
#include <chainparams.h>
|
||||
#include <node/eviction.h>
|
||||
#include <net.h>
|
||||
#include <net_processing.h>
|
||||
#include <netaddress.h>
|
||||
#include <netmessagemaker.h>
|
||||
#include <node/connection_types.h>
|
||||
#include <node/eviction.h>
|
||||
#include <protocol.h>
|
||||
#include <random.h>
|
||||
#include <serialize.h>
|
||||
#include <span.h>
|
||||
|
||||
#include <vector>
|
||||
@ -98,6 +102,17 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) co
|
||||
return complete;
|
||||
}
|
||||
|
||||
CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
{
|
||||
CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true);
|
||||
if (!node) return nullptr;
|
||||
node->SetCommonVersion(PROTOCOL_VERSION);
|
||||
peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK));
|
||||
node->fSuccessfullyConnected = true;
|
||||
AddTestNode(*node);
|
||||
return node;
|
||||
}
|
||||
|
||||
std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context)
|
||||
{
|
||||
std::vector<NodeEvictionCandidate> candidates;
|
||||
|
@ -6,16 +6,30 @@
|
||||
#define BITCOIN_TEST_UTIL_NET_H
|
||||
|
||||
#include <compat.h>
|
||||
#include <node/eviction.h>
|
||||
#include <netaddress.h>
|
||||
#include <net.h>
|
||||
#include <net_permissions.h>
|
||||
#include <net_processing.h>
|
||||
#include <netaddress.h>
|
||||
#include <node/connection_types.h>
|
||||
#include <node/eviction.h>
|
||||
#include <sync.h>
|
||||
#include <util/sock.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class FastRandomContext;
|
||||
|
||||
template <typename C>
|
||||
class Span;
|
||||
|
||||
struct ConnmanTestMsg : public CConnman {
|
||||
using CConnman::CConnman;
|
||||
@ -25,11 +39,20 @@ struct ConnmanTestMsg : public CConnman {
|
||||
m_peer_connect_timeout = timeout;
|
||||
}
|
||||
|
||||
std::vector<CNode*> TestNodes()
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
return m_nodes;
|
||||
}
|
||||
|
||||
void AddTestNode(CNode& node)
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
m_nodes.push_back(&node);
|
||||
|
||||
if (node.IsManualOrFullOutboundConn()) ++m_network_conn_counts[node.addr.GetNetwork()];
|
||||
}
|
||||
|
||||
void ClearTestNodes()
|
||||
{
|
||||
LOCK(m_nodes_mutex);
|
||||
@ -53,6 +76,11 @@ struct ConnmanTestMsg : public CConnman {
|
||||
|
||||
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const;
|
||||
void FlushSendBuffer(CNode& node) const;
|
||||
|
||||
bool AlreadyConnectedPublic(const CAddress& addr) { return AlreadyConnectedToAddress(addr); };
|
||||
|
||||
CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
|
||||
};
|
||||
|
||||
constexpr ServiceFlags ALL_SERVICE_FLAGS[]{
|
||||
|
@ -90,6 +90,11 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False)
|
||||
_, txid, tx_hex = self.check_p2p_tx_violation()
|
||||
|
||||
self.log.info("Tests with node in normal mode with block-relay-only connection, sending an inv")
|
||||
conn = self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="block-relay-only")
|
||||
assert_equal(self.nodes[0].getpeerinfo()[0]['relaytxes'], False)
|
||||
self.check_p2p_inv_violation(conn)
|
||||
|
||||
self.log.info("Check that txs from RPC are not sent to blockrelay connection")
|
||||
conn = self.nodes[0].add_outbound_p2p_connection(P2PTxInvStore(), p2p_idx=1, connection_type="block-relay-only")
|
||||
|
||||
@ -101,6 +106,13 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
conn.sync_send_with_ping()
|
||||
assert(int(txid, 16) not in conn.get_invs())
|
||||
|
||||
def check_p2p_inv_violation(self, peer):
|
||||
self.log.info("Check that tx-invs from P2P are rejected and result in disconnect")
|
||||
with self.nodes[0].assert_debug_log(["inv sent in violation of protocol, disconnecting peer"]):
|
||||
peer.send_message(msg_inv([CInv(t=MSG_TX, h=0x12345)]))
|
||||
peer.wait_for_disconnect()
|
||||
self.nodes[0].disconnect_p2ps()
|
||||
|
||||
def check_p2p_tx_violation(self):
|
||||
self.log.info('Check that txs from P2P are rejected and result in disconnect')
|
||||
spendtx = self.miniwallet.create_self_transfer(from_node=self.nodes[0])
|
||||
@ -109,9 +121,7 @@ class P2PBlocksOnly(BitcoinTestFramework):
|
||||
self.nodes[0].p2ps[0].send_message(msg_tx(spendtx['tx']))
|
||||
self.nodes[0].p2ps[0].wait_for_disconnect()
|
||||
assert_equal(self.nodes[0].getmempoolinfo()['size'], 0)
|
||||
|
||||
# Remove the disconnected peer
|
||||
del self.nodes[0].p2ps[0]
|
||||
self.nodes[0].disconnect_p2ps()
|
||||
|
||||
return spendtx['tx'], spendtx['txid'], spendtx['hex']
|
||||
|
||||
|
@ -239,8 +239,11 @@ class NetTest(DashTestFramework):
|
||||
# add a node (node2) to node0
|
||||
ip_port = "127.0.0.1:{}".format(p2p_port(2))
|
||||
self.nodes[0].addnode(node=ip_port, command='add')
|
||||
# try to add an equivalent ip
|
||||
ip_port2 = "127.1:{}".format(p2p_port(2))
|
||||
assert_raises_rpc_error(-23, "Node already added", self.nodes[0].addnode, node=ip_port2, command='add')
|
||||
# check that the node has indeed been added
|
||||
added_nodes = self.nodes[0].getaddednodeinfo(ip_port)
|
||||
added_nodes = self.nodes[0].getaddednodeinfo()
|
||||
assert_equal(len(added_nodes), 1)
|
||||
assert_equal(added_nodes[0]['addednode'], ip_port)
|
||||
# check that node cannot be added again
|
||||
|
Loading…
Reference in New Issue
Block a user