mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-12 15:09:59 +01:00
Merge #19697: Improvements on ADDR caching
0d04784af1Refactor the functional test (Gleb Naumenko)83ad65f31bAddress nits in ADDR caching (Gleb Naumenko)81b00f8780Add indexing ADDR cache by local socket addr (Gleb Naumenko)42ec558542Justify the choice of ADDR cache lifetime (Gleb Naumenko) Pull request description: This is a follow-up on #18991 which does 3 things: - improves privacy of a node listening to multiple addresses via adding cache index by local socket addr (suggested [here](https://github.com/bitcoin/bitcoin/pull/18991#issuecomment-668219345)) - documents on the choice of 24h cache lifetime - addresses nits from #18991 ACKs for top commit: jnewbery: utACK0d04784af1vasild: ACK0d04784jonatack: Code review ACK0d04784Tree-SHA512: bb65a34dd1ce2811186d3e4469bc33e8399cebaaa494ce13041c7cff23275870e4176a719f7a72f8d779c49f8b2344bf4fa1aeb3ea4e2626d5ae76514f00a750
This commit is contained in:
45
src/net.cpp
45
src/net.cpp
@@ -94,6 +94,7 @@ const std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
|
||||
|
||||
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
||||
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
||||
//
|
||||
// Global state variables
|
||||
//
|
||||
@@ -2560,15 +2561,47 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
|
||||
return addresses;
|
||||
}
|
||||
|
||||
std::vector<CAddress> CConnman::GetAddresses(Network requestor_network, size_t max_addresses, size_t max_pct)
|
||||
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
||||
{
|
||||
SOCKET socket;
|
||||
WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
|
||||
auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
|
||||
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
||||
.Write(requestor.addr.GetNetwork())
|
||||
.Write(local_socket_bytes.data(), local_socket_bytes.size())
|
||||
.Finalize();
|
||||
const auto current_time = GetTime<std::chrono::microseconds>();
|
||||
if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() ||
|
||||
m_addr_response_caches[requestor_network].m_update_addr_response < current_time) {
|
||||
m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
|
||||
m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
|
||||
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
||||
CachedAddrResponse& cache_entry = r.first->second;
|
||||
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
||||
cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
|
||||
// Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
|
||||
// and the usefulness of ADDR responses to honest users.
|
||||
//
|
||||
// Longer cache lifetime makes it more difficult for an attacker to scrape
|
||||
// enough AddrMan data to maliciously infer something useful.
|
||||
// By the time an attacker scraped enough AddrMan records, most of
|
||||
// the records should be old enough to not leak topology info by
|
||||
// e.g. analyzing real-time changes in timestamps.
|
||||
//
|
||||
// It takes only several hundred requests to scrape everything from an AddrMan containing 100,000 nodes,
|
||||
// so ~24 hours of cache lifetime indeed makes the data less inferable by the time
|
||||
// most of it could be scraped (considering that timestamps are updated via
|
||||
// ADDR self-announcements and when nodes communicate).
|
||||
// We also should be robust to those attacks which may not require scraping *full* victim's AddrMan
|
||||
// (because even several timestamps of the same handful of nodes may leak privacy).
|
||||
//
|
||||
// On the other hand, longer cache lifetime makes ADDR responses
|
||||
// outdated and less useful for an honest requestor, e.g. if most nodes
|
||||
// in the ADDR response are no longer active.
|
||||
//
|
||||
// However, the churn in the network is known to be rather low. Since we consider
|
||||
// nodes to be "terrible" (see IsTerrible()) if the timestamps are older than 30 days,
|
||||
// max. 24 hours of "penalty" due to cache shouldn't make any meaningful difference
|
||||
// in terms of the freshness of the response.
|
||||
cache_entry.m_cache_entry_expiration = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
|
||||
}
|
||||
return m_addr_response_caches[requestor_network].m_addrs_response_cache;
|
||||
return cache_entry.m_addrs_response_cache;
|
||||
}
|
||||
|
||||
bool CConnman::AddNode(const std::string& strNode)
|
||||
|
||||
Reference in New Issue
Block a user