BIP155 (addrv2)
Tor v3 + i2p
This commit is contained in:
220
src/addrman.h
220
src/addrman.h
@@ -24,6 +24,7 @@
|
||||
#include "protocol.h"
|
||||
#include "random.h"
|
||||
#include "sync.h"
|
||||
#include "streams.h"
|
||||
#include "timedata.h"
|
||||
#include "util.h"
|
||||
#include "fs.h"
|
||||
@@ -64,18 +65,17 @@ private:
|
||||
//! position in vRandom
|
||||
int nRandomPos;
|
||||
|
||||
//! Address is local
|
||||
bool fLocal;
|
||||
|
||||
friend class CAddrMan;
|
||||
|
||||
public:
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(*(CAddress*)this);
|
||||
READWRITE(source);
|
||||
READWRITE(nLastSuccess);
|
||||
READWRITE(nAttempts);
|
||||
SERIALIZE_METHODS(CAddrInfo, obj)
|
||||
{
|
||||
READWRITEAS(CAddress, obj);
|
||||
READ_WRITE(obj.source, obj.nLastSuccess, obj.nAttempts);
|
||||
}
|
||||
|
||||
void Init()
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
nRefCount = 0;
|
||||
fInTried = false;
|
||||
nRandomPos = -1;
|
||||
fLocal = false;
|
||||
}
|
||||
|
||||
CAddrInfo(const CAddress &addrIn, const CNetAddr &addrSource) : CAddress(addrIn), source(addrSource)
|
||||
@@ -116,9 +117,15 @@ public:
|
||||
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
|
||||
bool IsTerrible(int64_t nNow = GetTime()) const;
|
||||
|
||||
//Determine if this entry was just tried
|
||||
bool IsJustTried(int64_t nNow = GetTime()) const;
|
||||
|
||||
//! Calculate the relative chance this entry should be given when selecting nodes to connect to
|
||||
double GetChance(int64_t nNow = GetTime()) const;
|
||||
|
||||
//Returns the last successful connection
|
||||
int64_t GetLastSuccess() {return nTime;}
|
||||
|
||||
};
|
||||
|
||||
/** Stochastic address manager
|
||||
@@ -199,8 +206,30 @@ private:
|
||||
//! critical section to protect the inner data structures
|
||||
mutable CCriticalSection cs;
|
||||
|
||||
//! Serialization versions.
|
||||
enum Format : uint8_t {
|
||||
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
|
||||
V1_DETERMINISTIC = 1, //!< for pre-asmap files
|
||||
V2_ASMAP = 2, //!< for files including asmap version
|
||||
V3_BIP155 = 3, //!< same as V2_ASMAP plus addresses are in BIP155 format
|
||||
};
|
||||
|
||||
//! The maximum format this software knows it can unserialize. Also, we always serialize
|
||||
//! in this format.
|
||||
//! The format (first byte in the serialized stream) can be higher than this and
|
||||
//! still this software may be able to unserialize the file - if the second byte
|
||||
//! (see `lowest_compatible` in `Unserialize()`) is less or equal to this.
|
||||
static constexpr Format FILE_FORMAT = Format::V3_BIP155;
|
||||
|
||||
//! The initial value of a field that is incremented every time an incompatible format
|
||||
//! change is made (such that old software versions would not be able to parse and
|
||||
//! understand the new file format). This is 32 because we overtook the "key size"
|
||||
//! field which was 32 historically.
|
||||
//! @note Don't increment this. Increment `lowest_compatible` in `Serialize()` instead.
|
||||
static constexpr uint8_t INCOMPATIBILITY_BASE = 32;
|
||||
|
||||
//! last used nId
|
||||
int nIdCount;
|
||||
int nIdCount GUARDED_BY(cs){0};
|
||||
|
||||
//! table with information about all nIds
|
||||
std::map<int, CAddrInfo> mapInfo;
|
||||
@@ -280,12 +309,16 @@ protected:
|
||||
#endif
|
||||
|
||||
//! Select several addresses at once.
|
||||
void GetAddr_(std::vector<CAddress> &vAddr);
|
||||
void GetAddr_(std::vector<CAddress> &vAddr, bool wants_addrv2);
|
||||
|
||||
//! Mark an entry as currently-connected-to.
|
||||
void Connected_(const CService &addr, int64_t nTime);
|
||||
|
||||
//! Mark an entry as local
|
||||
void SetLocal_(const CService &addr);
|
||||
|
||||
public:
|
||||
void GetAllPeers(std::map<std::string, int64_t> &info);
|
||||
// Compressed IP->ASN mapping, loaded from a file when a node starts.
|
||||
// Should be always empty if no file was provided.
|
||||
// This mapping is then used for bucketing nodes in Addrman.
|
||||
@@ -336,13 +369,22 @@ public:
|
||||
* very little in common.
|
||||
*/
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s) const
|
||||
void Serialize(Stream &s_) const
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!cs)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
unsigned char nVersion = 2;
|
||||
s << nVersion;
|
||||
s << ((unsigned char)32);
|
||||
// Always serialize in the latest version (FILE_FORMAT).
|
||||
|
||||
OverrideStream<Stream> s(&s_, s_.GetType(), s_.GetVersion() | ADDRV2_FORMAT);
|
||||
|
||||
s << static_cast<uint8_t>(FILE_FORMAT);
|
||||
|
||||
// Increment `lowest_compatible` iff a newly introduced format is incompatible with
|
||||
// the previous one.
|
||||
static constexpr uint8_t lowest_compatible = Format::V3_BIP155;
|
||||
s << static_cast<uint8_t>(INCOMPATIBILITY_BASE + lowest_compatible);
|
||||
|
||||
s << nKey;
|
||||
s << nNew;
|
||||
s << nTried;
|
||||
@@ -393,22 +435,40 @@ public:
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s)
|
||||
void Unserialize(Stream& s_)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
Clear();
|
||||
unsigned char nVersion;
|
||||
s >> nVersion;
|
||||
unsigned char nKeySize;
|
||||
s >> nKeySize;
|
||||
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
||||
assert(vRandom.empty());
|
||||
|
||||
Format format;
|
||||
s_ >> Using<CustomUintFormatter<1>>(format);
|
||||
|
||||
int stream_version = s_.GetVersion();
|
||||
if (format >= Format::V3_BIP155) {
|
||||
// Add ADDRV2_FORMAT to the version so that the CNetAddr and CAddress
|
||||
// unserialize methods know that an address in addrv2 format is coming.
|
||||
stream_version |= ADDRV2_FORMAT;
|
||||
}
|
||||
|
||||
OverrideStream<Stream> s(&s_, s_.GetType(), stream_version);
|
||||
|
||||
uint8_t compat;
|
||||
s >> compat;
|
||||
const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE;
|
||||
if (lowest_compatible > FILE_FORMAT) {
|
||||
throw std::ios_base::failure(strprintf(
|
||||
"Unsupported format of addrman database: %u. It is compatible with formats >=%u, "
|
||||
"but the maximum supported by this version of %s is %u.",
|
||||
format, lowest_compatible, PACKAGE_NAME, static_cast<uint8_t>(FILE_FORMAT)));
|
||||
}
|
||||
|
||||
s >> nKey;
|
||||
s >> nNew;
|
||||
s >> nTried;
|
||||
int nUBuckets = 0;
|
||||
s >> nUBuckets;
|
||||
if (nVersion != 0) {
|
||||
if (format >= Format::V1_DETERMINISTIC) {
|
||||
nUBuckets ^= (1 << 30);
|
||||
}
|
||||
|
||||
@@ -422,7 +482,7 @@ public:
|
||||
|
||||
// Deserialize entries from the new table.
|
||||
for (int n = 0; n < nNew; n++) {
|
||||
CAddrInfo &info = mapInfo[n];
|
||||
CAddrInfo& info = mapInfo[n];
|
||||
s >> info;
|
||||
mapAddr[info] = n;
|
||||
info.nRandomPos = vRandom.size();
|
||||
@@ -437,7 +497,7 @@ public:
|
||||
s >> info;
|
||||
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
|
||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||
if (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||
if (info.IsValid() && vvTried[nKBucket][nKBucketPos] == -1) {
|
||||
info.nRandomPos = vRandom.size();
|
||||
info.fInTried = true;
|
||||
vRandom.push_back(nIdCount);
|
||||
@@ -452,60 +512,84 @@ public:
|
||||
nTried -= nLost;
|
||||
|
||||
// Store positions in the new table buckets to apply later (if possible).
|
||||
std::map<int, int> entryToBucket; // Represents which entry belonged to which bucket when serializing
|
||||
// An entry may appear in up to ADDRMAN_NEW_BUCKETS_PER_ADDRESS buckets,
|
||||
// so we store all bucket-entry_index pairs to iterate through later.
|
||||
std::vector<std::pair<int, int>> bucket_entries;
|
||||
|
||||
for (int bucket = 0; bucket < nUBuckets; bucket++) {
|
||||
int nSize = 0;
|
||||
s >> nSize;
|
||||
for (int n = 0; n < nSize; n++) {
|
||||
int nIndex = 0;
|
||||
s >> nIndex;
|
||||
if (nIndex >= 0 && nIndex < nNew) {
|
||||
entryToBucket[nIndex] = bucket;
|
||||
for (int bucket = 0; bucket < nUBuckets; ++bucket) {
|
||||
int num_entries{0};
|
||||
s >> num_entries;
|
||||
for (int n = 0; n < num_entries; ++n) {
|
||||
int entry_index{0};
|
||||
s >> entry_index;
|
||||
if (entry_index >= 0 && entry_index < nNew) {
|
||||
bucket_entries.emplace_back(bucket, entry_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint256 supplied_asmap_version;
|
||||
// If the bucket count and asmap checksum haven't changed, then attempt
|
||||
// to restore the entries to the buckets/positions they were in before
|
||||
// serialization.
|
||||
uint256 supplied_asmap_checksum;
|
||||
if (m_asmap.size() != 0) {
|
||||
supplied_asmap_version = SerializeHash(m_asmap);
|
||||
supplied_asmap_checksum = SerializeHash(m_asmap);
|
||||
}
|
||||
uint256 serialized_asmap_version;
|
||||
if (nVersion > 1) {
|
||||
s >> serialized_asmap_version;
|
||||
uint256 serialized_asmap_checksum;
|
||||
if (format >= Format::V2_ASMAP) {
|
||||
s >> serialized_asmap_checksum;
|
||||
}
|
||||
const bool restore_bucketing{nUBuckets == ADDRMAN_NEW_BUCKET_COUNT &&
|
||||
serialized_asmap_checksum == supplied_asmap_checksum};
|
||||
|
||||
if (!restore_bucketing) {
|
||||
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
||||
}
|
||||
|
||||
for (int n = 0; n < nNew; n++) {
|
||||
CAddrInfo &info = mapInfo[n];
|
||||
int bucket = entryToBucket[n];
|
||||
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (nVersion == 2 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 &&
|
||||
info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS && serialized_asmap_version == supplied_asmap_version) {
|
||||
// Bucketing has not changed, using existing bucket positions for the new table
|
||||
vvNew[bucket][nUBucketPos] = n;
|
||||
info.nRefCount++;
|
||||
} else {
|
||||
// In case the new table data cannot be used (nVersion unknown, bucket count wrong or new asmap),
|
||||
// try to give them a reference based on their primary source address.
|
||||
LogPrint("addrman", "Bucketing method was updated, re-bucketing addrman entries from disk\n");
|
||||
bucket = info.GetNewBucket(nKey, m_asmap);
|
||||
nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (vvNew[bucket][nUBucketPos] == -1) {
|
||||
vvNew[bucket][nUBucketPos] = n;
|
||||
info.nRefCount++;
|
||||
for (auto bucket_entry : bucket_entries) {
|
||||
int bucket{bucket_entry.first};
|
||||
const int entry_index{bucket_entry.second};
|
||||
// CAddrInfo& info = mapInfo[entry_index];
|
||||
|
||||
const auto it{mapInfo.find(entry_index)};
|
||||
if (it != mapInfo.end()) {
|
||||
CAddrInfo& info = (*it).second;
|
||||
|
||||
// Don't store the entry in the new bucket if it's not a valid address for our addrman
|
||||
if (!info.IsValid()) continue;
|
||||
|
||||
// The entry shouldn't appear in more than
|
||||
// ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
|
||||
// this bucket_entry.
|
||||
if (info.nRefCount >= ADDRMAN_NEW_BUCKETS_PER_ADDRESS) continue;
|
||||
|
||||
int bucket_position = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (restore_bucketing && vvNew[bucket][bucket_position] == -1) {
|
||||
// Bucketing has not changed, using existing bucket positions for the new table
|
||||
vvNew[bucket][bucket_position] = entry_index;
|
||||
++info.nRefCount;
|
||||
} else {
|
||||
// In case the new table data cannot be used (bucket count wrong or new asmap),
|
||||
// try to give them a reference based on their primary source address.
|
||||
bucket = info.GetNewBucket(nKey, m_asmap);
|
||||
bucket_position = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (vvNew[bucket][bucket_position] == -1) {
|
||||
vvNew[bucket][bucket_position] = entry_index;
|
||||
++info.nRefCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prune new entries with refcount 0 (as a result of collisions).
|
||||
int nLostUnk = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
|
||||
for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
|
||||
if (it->second.fInTried == false && it->second.nRefCount == 0) {
|
||||
std::map<int, CAddrInfo>::const_iterator itCopy = it++;
|
||||
const auto itCopy = it++;
|
||||
Delete(itCopy->first);
|
||||
nLostUnk++;
|
||||
++nLostUnk;
|
||||
} else {
|
||||
it++;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (nLost + nLostUnk > 0) {
|
||||
@@ -531,7 +615,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
nIdCount = 0;
|
||||
nTried = 0;
|
||||
nNew = 0;
|
||||
mapInfo.clear();
|
||||
@@ -657,13 +740,13 @@ public:
|
||||
}
|
||||
|
||||
//! Return a bunch of addresses, selected at random.
|
||||
std::vector<CAddress> GetAddr()
|
||||
std::vector<CAddress> GetAddr(bool wants_addrv2 = false)
|
||||
{
|
||||
Check();
|
||||
std::vector<CAddress> vAddr;
|
||||
{
|
||||
LOCK(cs);
|
||||
GetAddr_(vAddr);
|
||||
GetAddr_(vAddr, wants_addrv2);
|
||||
}
|
||||
Check();
|
||||
return vAddr;
|
||||
@@ -680,6 +763,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
//! Mark an entry as currently-connected-to.
|
||||
void SetLocal(const CService &addr)
|
||||
{
|
||||
{
|
||||
LOCK(cs);
|
||||
Check();
|
||||
SetLocal_(addr);
|
||||
Check();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // HUSH_ADDRMAN_H
|
||||
|
||||
Reference in New Issue
Block a user