BIP155 (addrv2)

Tor v3 + i2p
This commit is contained in:
zanzibar
2023-01-06 15:21:08 +00:00
parent fe9f1ef9e4
commit 512da314a5
108 changed files with 8214 additions and 2173 deletions

View File

@@ -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