partial bitcoin#17428: Try to preserve outbound block-relay-only connections during restart

excludes:
- 0a85e5a7bc8dc6587963e2e37ac1b087a1fc97fe
This commit is contained in:
Kittywhiskers Van Gogh 2023-09-10 22:32:53 +05:30 committed by PastaPastaPasta
parent bda40398a5
commit 3feccd7925
5 changed files with 94 additions and 9 deletions

View File

@ -53,6 +53,7 @@ Subdirectory | File(s) | Description
`indexes/blockfilter/basic/db/` | LevelDB database | Blockfilter index LevelDB database for the basic filtertype; *optional*, used if `-blockfilterindex=basic`
`indexes/blockfilter/basic/` | `fltrNNNNN.dat`<sup>[\[2\]](#note2)</sup> | Blockfilter index filters for the basic filtertype; *optional*, used if `-blockfilterindex=basic`
`wallets/` | | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, a wallet resides in the data directory
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
`evodb/` | |special txes and quorums database
`llmq/` | |quorum signatures database
`./` | `banlist.dat` | Stores the IPs/subnets of banned nodes

View File

@ -9,6 +9,7 @@
#include <chainparams.h>
#include <clientversion.h>
#include <hash.h>
#include <logging/timer.h>
#include <random.h>
#include <streams.h>
#include <tinyformat.h>
@ -157,3 +158,22 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
}
return ret;
}
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
{
LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
SerializeFileDB("anchors", anchors_db_path, anchors);
}
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
if (DeserializeFileDB(anchors_db_path, anchors)) {
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
} else {
anchors.clear();
}
fs::remove(anchors_db_path);
return anchors;
}

View File

@ -11,9 +11,9 @@
#include <serialize.h>
#include <string>
#include <map>
#include <vector>
class CSubNet;
class CAddress;
class CAddrMan;
class CDataStream;
@ -73,4 +73,20 @@ public:
bool Read(banmap_t& banSet);
};
/**
* Dump the anchor IP address database (anchors.dat)
*
* Anchors are last known outgoing block-relay-only peers that are
* tried to re-connect to on startup.
*/
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors);
/**
* Read the anchor IP address database (anchors.dat)
*
* Deleting anchors.dat is intentional as it avoids renewed peering to anchors after
* an unclean shutdown and thus potential exploitation of the anchor peer policy.
*/
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path);
#endif // BITCOIN_ADDRDB_H

View File

@ -64,6 +64,12 @@
#include <math.h>
/** Maximum number of block-relay-only anchor connections */
static constexpr size_t MAX_BLOCK_RELAY_ONLY_ANCHORS = 2;
static_assert (MAX_BLOCK_RELAY_ONLY_ANCHORS <= static_cast<size_t>(MAX_BLOCK_RELAY_ONLY_CONNECTIONS), "MAX_BLOCK_RELAY_ONLY_ANCHORS must not exceed MAX_BLOCK_RELAY_ONLY_CONNECTIONS.");
/** Anchor IP address database file name */
const char* const ANCHORS_DATABASE_FILENAME = "anchors.dat";
// How often to dump addresses to peers.dat
static constexpr std::chrono::minutes DUMP_PEERS_INTERVAL{15};
@ -2336,6 +2342,13 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
int nTries = 0;
while (!interruptNet)
{
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
// already-connected network ranges, ...) before trying new addrman addresses.
nTries++;
if (nTries > 100)
break;
CAddrInfo addr = addrman.SelectTriedCollision();
// SelectTriedCollision returns an invalid address if it is empty.
@ -2365,13 +2378,6 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
break;
}
// If we didn't find an appropriate destination after trying 100 addresses fetched from addrman,
// stop this loop, and let the outer loop run again (which sleeps, adds seed nodes, recalculates
// already-connected network ranges, ...) before trying new addrman addresses.
nTries++;
if (nTries > 100)
break;
if (!IsReachable(addr))
continue;
@ -2427,6 +2433,19 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
}
std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
{
std::vector<CAddress> ret;
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
if (pnode->IsBlockRelayOnly()) {
ret.push_back(pnode->addr);
}
}
return ret;
}
std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
{
std::vector<AddedNodeInfo> ret;
@ -3131,6 +3150,15 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
}
if (m_use_addrman_outgoing) {
// Load addresses from anchors.dat
m_anchors = ReadAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME);
if (m_anchors.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
m_anchors.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
LogPrintf("%i block-relay-only anchors will be tried for connections.\n", m_anchors.size());
}
uiInterface.InitMessage(_("Starting network threads...").translated);
fAddressesInitialized = true;
@ -3311,6 +3339,15 @@ void CConnman::StopNodes()
if (fAddressesInitialized) {
DumpAddresses();
fAddressesInitialized = false;
if (m_use_addrman_outgoing) {
// Anchor connections are only dumped during clean shutdown.
std::vector<CAddress> anchors_to_dump = GetCurrentBlockRelayOnlyConns();
if (anchors_to_dump.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
anchors_to_dump.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
DumpAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
}
}
{

View File

@ -572,6 +572,11 @@ private:
void RecordBytesRecv(uint64_t bytes);
void RecordBytesSent(uint64_t bytes);
/**
* Return vector of current BLOCK_RELAY peers.
*/
std::vector<CAddress> GetCurrentBlockRelayOnlyConns() const;
// Whether the node should be passed out in ForEach* callbacks
static bool NodeFullyConnected(const CNode* pnode);
@ -679,6 +684,12 @@ private:
NetEventsInterface* m_msgproc;
BanMan* m_banman;
/**
* Addresses that were saved during the previous clean shutdown. We'll
* attempt to make block-relay-only connections to them.
*/
std::vector<CAddress> m_anchors;
/** SipHasher seeds for deterministic randomness */
const uint64_t nSeed0, nSeed1;