Skip to content

Commit 3b3b1b8

Browse files
bde72a4 merge bitcoin#23324: print peer counts for all reachable networks in -netinfo (Kittywhiskers Van Gogh) 4b24544 merge bitcoin#22959: Display all proxies in -getinfo (Kittywhiskers Van Gogh) 30b0fcf merge bitcoin#22544: drop torv2; torv3 becomes onion per GetNetworkName() (Kittywhiskers Van Gogh) b6ca36e merge bitcoin#22547: Add progress bar for -getinfo (Kittywhiskers Van Gogh) 1f89bfd merge bitcoin#21832: Implement human readable -getinfo (Kittywhiskers Van Gogh) 2200b78 merge bitcoin#20877: user help and argument parsing improvements (Kittywhiskers Van Gogh) bd934c7 partial bitcoin#20764: cli -netinfo peer connections dashboard updates (Kittywhiskers Van Gogh) b2d8656 merge bitcoin#21261: update inbound eviction protection for multiple networks, add I2P peers (Kittywhiskers Van Gogh) 0b16b50 cli: fix loop counter comparison in `ProcessReply` (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Dependency for dashpay#6035 * Dependency for dashpay#6031 * In [dash#5904](dashpay#5904) ([bitcoin#21595](bitcoin#21595)), one of the loops in `ProcessReply` is supposed to iterate `rows.size()` times (which at the time was hardcoded to `3`), the backport erroneously set the value to `m_networks.size()` (which also evaluated to `3`) as part of increasing `m_networks.size()` usage. As this pull request includes [bitcoin#23324](bitcoin#23324), which changes it over to `rows.size()`, the above has been corrected in a separate commit for documentation purposes. * `-addrinfo` output ![dash-cli addrinfo output](https://github.com/dashpay/dash/assets/63189531/24db46be-729e-4fa8-a268-87f2497cff9a) * `-getinfo` output (diamonds are due to rendering limitations of my terminal and are not indicative of the symbols used) ![dash-cli getinfo output](https://github.com/dashpay/dash/assets/63189531/626fe67f-f505-4a04-931a-76e75146e5a0) * `-netinfo` output ![dash-cli netinfo output](https://github.com/dashpay/dash/assets/63189531/afbff3d0-7127-44e2-bfe7-81b08c0e214e) ## Breaking Changes * CLI `-addrinfo` now returns a single field for the number of `onion` addresses known to the node instead of separate `torv2` and `torv3` fields, as support for TorV2 addresses was removed from Dash Core in 18.0. * `-getinfo` has been updated to return data in a user-friendly format that also reduces vertical space. ## 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 **(note: N/A)** - [x] I have assigned this pull request to a milestone ACKs for top commit: PastaPastaPasta: utACK bde72a4 Tree-SHA512: 921cb45b7e243a321a32c835eb23d5ba8df610ff234a548a9051436a2c21845ce70097fb9a9bb812b77b04373f9f0a9f90264168d97b08da1890be06bfd9f99c
2 parents cb352c5 + bde72a4 commit 3b3b1b8

File tree

10 files changed

+870
-330
lines changed

10 files changed

+870
-330
lines changed

doc/release-notes-21832.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Tools and Utilities
2+
-------------------
3+
4+
- Update `-getinfo` to return data in a user-friendly format that also reduces vertical space.

doc/release-notes-22544.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Tools and Utilities
2+
-------------------
3+
4+
- CLI `-addrinfo` now returns a single field for the number of `onion` addresses
5+
known to the node instead of separate `torv2` and `torv3` fields, as support
6+
for Tor V2 addresses was removed from Dash Core in 18.0.

doc/tor.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ There are several ways to see your local onion address in Dash Core:
1818
You may set the `-debug=tor` config logging option to have additional
1919
information in the debug log about your Tor configuration.
2020

21-
CLI `-addrinfo` returns the number of addresses known to your node per network
22-
type, including Tor v2 and v3. This is useful to see how many onion addresses
23-
are known to your node for `-onlynet=onion` and how many Tor v3 addresses it
24-
knows when upgrading to current and future Tor releases that support Tor v3 only.
21+
CLI `-addrinfo` returns the number of addresses known to your node per
22+
network. This can be useful to see how many onion peers your node knows,
23+
e.g. for `-onlynet=onion`.
2524

2625
## 1. Run Dash Core behind a Tor proxy
2726

src/bitcoin-cli.cpp

Lines changed: 279 additions & 85 deletions
Large diffs are not rendered by default.

src/net.cpp

Lines changed: 81 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#endif
6363

6464
#include <algorithm>
65+
#include <array>
6566
#include <cstdint>
6667
#include <functional>
6768
#include <unordered_map>
@@ -913,18 +914,6 @@ static bool ReverseCompareNodeTimeConnected(const NodeEvictionCandidate& a, cons
913914
return a.nTimeConnected > b.nTimeConnected;
914915
}
915916

916-
static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
917-
{
918-
if (a.m_is_local != b.m_is_local) return b.m_is_local;
919-
return a.nTimeConnected > b.nTimeConnected;
920-
}
921-
922-
static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
923-
{
924-
if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
925-
return a.nTimeConnected > b.nTimeConnected;
926-
}
927-
928917
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
929918
return a.nKeyedNetGroup < b.nKeyedNetGroup;
930919
}
@@ -955,6 +944,26 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
955944
return a.nTimeConnected > b.nTimeConnected;
956945
}
957946

947+
/**
948+
* Sort eviction candidates by network/localhost and connection uptime.
949+
* Candidates near the beginning are more likely to be evicted, and those
950+
* near the end are more likely to be protected, e.g. less likely to be evicted.
951+
* - First, nodes that are not `is_local` and that do not belong to `network`,
952+
* sorted by increasing uptime (from most recently connected to connected longer).
953+
* - Then, nodes that are `is_local` or belong to `network`, sorted by increasing uptime.
954+
*/
955+
struct CompareNodeNetworkTime {
956+
const bool m_is_local;
957+
const Network m_network;
958+
CompareNodeNetworkTime(bool is_local, Network network) : m_is_local(is_local), m_network(network) {}
959+
bool operator()(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b) const
960+
{
961+
if (m_is_local && a.m_is_local != b.m_is_local) return b.m_is_local;
962+
if ((a.m_network == m_network) != (b.m_network == m_network)) return b.m_network == m_network;
963+
return a.nTimeConnected > b.nTimeConnected;
964+
};
965+
};
966+
958967
//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
959968
template <typename T, typename Comparator>
960969
static void EraseLastKElements(
@@ -966,40 +975,72 @@ static void EraseLastKElements(
966975
elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
967976
}
968977

969-
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
978+
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& eviction_candidates)
970979
{
971980
// Protect the half of the remaining nodes which have been connected the longest.
972981
// This replicates the non-eviction implicit behavior, and precludes attacks that start later.
973-
// To favorise the diversity of our peer connections, reserve up to (half + 2) of
974-
// these protected spots for onion and localhost peers, if any, even if they're not
975-
// longest uptime overall. This helps protect tor peers, which tend to be otherwise
982+
// To favorise the diversity of our peer connections, reserve up to half of these protected
983+
// spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
984+
// This helps protect these higher-latency peers that tend to be otherwise
976985
// disadvantaged under our eviction criteria.
977-
const size_t initial_size = vEvictionCandidates.size();
978-
size_t total_protect_size = initial_size / 2;
979-
const size_t onion_protect_size = total_protect_size / 2;
980-
981-
if (onion_protect_size) {
982-
// Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
983-
EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
984-
[](const NodeEvictionCandidate& n) { return n.m_is_onion; });
985-
}
986-
987-
const size_t localhost_min_protect_size{2};
988-
if (onion_protect_size >= localhost_min_protect_size) {
989-
// Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
990-
// to localhost peers, sorted by longest uptime, as manually configured
991-
// hidden services not using `-bind=addr[:port]=onion` will not be detected
992-
// as inbound onion connections.
993-
const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
994-
const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
995-
EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
996-
[](const NodeEvictionCandidate& n) { return n.m_is_local; });
986+
const size_t initial_size = eviction_candidates.size();
987+
const size_t total_protect_size{initial_size / 2};
988+
989+
// Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
990+
// array members have first opportunity to recover unused slots from the previous iteration.
991+
struct Net { bool is_local; Network id; size_t count; };
992+
std::array<Net, 3> networks{
993+
{{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
994+
995+
// Count and store the number of eviction candidates per network.
996+
for (Net& n : networks) {
997+
n.count = std::count_if(eviction_candidates.cbegin(), eviction_candidates.cend(),
998+
[&n](const NodeEvictionCandidate& c) {
999+
return n.is_local ? c.m_is_local : c.m_network == n.id;
1000+
});
1001+
}
1002+
// Sort `networks` by ascending candidate count, to give networks having fewer candidates
1003+
// the first opportunity to recover unused protected slots from the previous iteration.
1004+
std::stable_sort(networks.begin(), networks.end(), [](Net a, Net b) { return a.count < b.count; });
1005+
1006+
// Protect up to 25% of the eviction candidates by disadvantaged network.
1007+
const size_t max_protect_by_network{total_protect_size / 2};
1008+
size_t num_protected{0};
1009+
1010+
while (num_protected < max_protect_by_network) {
1011+
const size_t disadvantaged_to_protect{max_protect_by_network - num_protected};
1012+
const size_t protect_per_network{
1013+
std::max(disadvantaged_to_protect / networks.size(), static_cast<size_t>(1))};
1014+
1015+
// Early exit flag if there are no remaining candidates by disadvantaged network.
1016+
bool protected_at_least_one{false};
1017+
1018+
for (const Net& n : networks) {
1019+
if (n.count == 0) continue;
1020+
const size_t before = eviction_candidates.size();
1021+
EraseLastKElements(eviction_candidates, CompareNodeNetworkTime(n.is_local, n.id),
1022+
protect_per_network, [&n](const NodeEvictionCandidate& c) {
1023+
return n.is_local ? c.m_is_local : c.m_network == n.id;
1024+
});
1025+
const size_t after = eviction_candidates.size();
1026+
if (before > after) {
1027+
protected_at_least_one = true;
1028+
num_protected += before - after;
1029+
if (num_protected >= max_protect_by_network) {
1030+
break;
1031+
}
1032+
}
1033+
}
1034+
if (!protected_at_least_one) {
1035+
break;
1036+
}
9971037
}
9981038

9991039
// Calculate how many we removed, and update our total number of peers that
10001040
// we want to protect based on uptime accordingly.
1001-
total_protect_size -= initial_size - vEvictionCandidates.size();
1002-
EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
1041+
assert(num_protected == initial_size - eviction_candidates.size());
1042+
const size_t remaining_to_protect{total_protect_size - num_protected};
1043+
EraseLastKElements(eviction_candidates, ReverseCompareNodeTimeConnected, remaining_to_protect);
10031044
}
10041045

10051046
[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
@@ -1016,8 +1057,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvict
10161057
// An attacker cannot manipulate this metric without performing useful work.
10171058
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
10181059
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
1019-
const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
1020-
EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
1060+
EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
10211061
[](const NodeEvictionCandidate& n) { return !n.m_relay_txs && n.fRelevantServices; });
10221062

10231063
// Protect 4 nodes that most recently sent us novel blocks.
@@ -1109,7 +1149,7 @@ bool CConnman::AttemptToEvictConnection()
11091149
HasAllDesirableServiceFlags(node->nServices),
11101150
node->m_relays_txs.load(), node->m_bloom_filter_loaded.load(),
11111151
node->nKeyedNetGroup, node->m_prefer_evict, node->addr.IsLocal(),
1112-
node->m_inbound_onion};
1152+
node->ConnectedThroughNetwork()};
11131153
vEvictionCandidates.push_back(candidate);
11141154
}
11151155
}

src/net.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,7 +1583,7 @@ struct NodeEvictionCandidate
15831583
uint64_t nKeyedNetGroup;
15841584
bool prefer_evict;
15851585
bool m_is_local;
1586-
bool m_is_onion;
1586+
Network m_network;
15871587
};
15881588

15891589
/**
@@ -1613,20 +1613,20 @@ size_t GetRequestedObjectCount(NodeId nodeId) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
16131613
* longest, to replicate the non-eviction implicit behavior and preclude attacks
16141614
* that start later.
16151615
*
1616-
* Half of these protected spots (1/4 of the total) are reserved for onion peers
1617-
* connected via our tor control service, if any, sorted by longest uptime, even
1618-
* if they're not longest uptime overall. Any remaining slots of the 1/4 are
1619-
* then allocated to protect localhost peers, if any (or up to 2 localhost peers
1620-
* if no slots remain and 2 or more onion peers were protected), sorted by
1621-
* longest uptime, as manually configured hidden services not using
1622-
* `-bind=addr[:port]=onion` will not be detected as inbound onion connections.
1616+
* Half of these protected spots (1/4 of the total) are reserved for the
1617+
* following categories of peers, sorted by longest uptime, even if they're not
1618+
* longest uptime overall:
1619+
*
1620+
* - onion peers connected via our tor control service
1621+
*
1622+
* - localhost peers, as manually configured hidden services not using
1623+
* `-bind=addr[:port]=onion` will not be detected as inbound onion connections
16231624
*
1624-
* This helps protect onion peers, which tend to be otherwise disadvantaged
1625-
* under our eviction criteria for their higher min ping times relative to IPv4
1626-
* and IPv6 peers, and favorise the diversity of peer connections.
1625+
* - I2P peers
16271626
*
1628-
* This function was extracted from SelectNodeToEvict() to be able to test the
1629-
* ratio-based protection logic deterministically.
1627+
* This helps protect these privacy network peers, which tend to be otherwise
1628+
* disadvantaged under our eviction criteria for their higher min ping times
1629+
* relative to IPv4/IPv6 peers, and favorise the diversity of peer connections.
16301630
*/
16311631
void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates);
16321632

src/test/fuzz/node_eviction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ FUZZ_TARGET(node_eviction)
3131
/* nKeyedNetGroup */ fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
3232
/* prefer_evict */ fuzzed_data_provider.ConsumeBool(),
3333
/* m_is_local */ fuzzed_data_provider.ConsumeBool(),
34-
/* m_is_onion */ fuzzed_data_provider.ConsumeBool(),
34+
/* m_network */ fuzzed_data_provider.PickValueInArray(ALL_NETWORKS),
3535
});
3636
}
3737
// Make a copy since eviction_candidates may be in some valid but otherwise

0 commit comments

Comments
 (0)