mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-11 12:22:39 -03:00
Integrate ASN bucketing in Addrman and add tests
Instead of using /16 netgroups to bucket nodes in Addrman for connection diversification, ASN, which better represents an actor in terms of network-layer infrastructure, is used. For testing, asmap.raw is used. It represents a minimal asmap needed for testing purposes.
This commit is contained in:
parent
8feb4e4b66
commit
ec45646de9
11 changed files with 483 additions and 88 deletions
|
@ -53,7 +53,8 @@ JSON_TEST_FILES = \
|
||||||
test/data/tx_invalid.json \
|
test/data/tx_invalid.json \
|
||||||
test/data/tx_valid.json
|
test/data/tx_valid.json
|
||||||
|
|
||||||
RAW_TEST_FILES =
|
RAW_TEST_FILES = \
|
||||||
|
test/data/asmap.raw
|
||||||
|
|
||||||
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
|
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
|
||||||
|
|
||||||
|
@ -429,3 +430,12 @@ endif
|
||||||
echo "};};"; \
|
echo "};};"; \
|
||||||
} > "$@.new" && mv -f "$@.new" "$@"
|
} > "$@.new" && mv -f "$@.new" "$@"
|
||||||
@echo "Generated $@"
|
@echo "Generated $@"
|
||||||
|
|
||||||
|
%.raw.h: %.raw
|
||||||
|
@$(MKDIR_P) $(@D)
|
||||||
|
@{ \
|
||||||
|
echo "static unsigned const char $(*F)_raw[] = {" && \
|
||||||
|
$(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \
|
||||||
|
echo "};"; \
|
||||||
|
} > "$@.new" && mv -f "$@.new" "$@"
|
||||||
|
@echo "Generated $@"
|
||||||
|
|
|
@ -8,17 +8,17 @@
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
|
|
||||||
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
|
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
|
||||||
{
|
{
|
||||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
|
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
|
||||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
|
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
|
||||||
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
|
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const
|
int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
|
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
|
||||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetCheapHash();
|
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
|
||||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
|
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
|
||||||
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
|
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
|
||||||
assert(info.nRefCount == 0);
|
assert(info.nRefCount == 0);
|
||||||
|
|
||||||
// which tried bucket to move the entry to
|
// which tried bucket to move the entry to
|
||||||
int nKBucket = info.GetTriedBucket(nKey);
|
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
|
||||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||||
|
|
||||||
// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
|
// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
|
||||||
|
@ -169,7 +169,7 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId)
|
||||||
nTried--;
|
nTried--;
|
||||||
|
|
||||||
// find which new bucket it belongs to
|
// find which new bucket it belongs to
|
||||||
int nUBucket = infoOld.GetNewBucket(nKey);
|
int nUBucket = infoOld.GetNewBucket(nKey, m_asmap);
|
||||||
int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
|
int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
|
||||||
ClearNew(nUBucket, nUBucketPos);
|
ClearNew(nUBucket, nUBucketPos);
|
||||||
assert(vvNew[nUBucket][nUBucketPos] == -1);
|
assert(vvNew[nUBucket][nUBucketPos] == -1);
|
||||||
|
@ -233,7 +233,7 @@ void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// which tried bucket to move the entry to
|
// which tried bucket to move the entry to
|
||||||
int tried_bucket = info.GetTriedBucket(nKey);
|
int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
|
||||||
int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
|
int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
|
||||||
|
|
||||||
// Will moving this address into tried evict another entry?
|
// Will moving this address into tried evict another entry?
|
||||||
|
@ -301,7 +301,7 @@ bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimeP
|
||||||
fNew = true;
|
fNew = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nUBucket = pinfo->GetNewBucket(nKey, source);
|
int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
|
||||||
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
|
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
|
||||||
if (vvNew[nUBucket][nUBucketPos] != nId) {
|
if (vvNew[nUBucket][nUBucketPos] != nId) {
|
||||||
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
|
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
|
||||||
|
@ -439,7 +439,7 @@ int CAddrMan::Check_()
|
||||||
if (vvTried[n][i] != -1) {
|
if (vvTried[n][i] != -1) {
|
||||||
if (!setTried.count(vvTried[n][i]))
|
if (!setTried.count(vvTried[n][i]))
|
||||||
return -11;
|
return -11;
|
||||||
if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey) != n)
|
if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
|
||||||
return -17;
|
return -17;
|
||||||
if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
|
if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
|
||||||
return -18;
|
return -18;
|
||||||
|
@ -545,7 +545,7 @@ void CAddrMan::ResolveCollisions_()
|
||||||
CAddrInfo& info_new = mapInfo[id_new];
|
CAddrInfo& info_new = mapInfo[id_new];
|
||||||
|
|
||||||
// Which tried bucket to move the entry to.
|
// Which tried bucket to move the entry to.
|
||||||
int tried_bucket = info_new.GetTriedBucket(nKey);
|
int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
|
||||||
int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
|
int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
|
||||||
if (!info_new.IsValid()) { // id_new may no longer map to a valid address
|
if (!info_new.IsValid()) { // id_new may no longer map to a valid address
|
||||||
erase_collision = true;
|
erase_collision = true;
|
||||||
|
@ -609,10 +609,33 @@ CAddrInfo CAddrMan::SelectTriedCollision_()
|
||||||
CAddrInfo& newInfo = mapInfo[id_new];
|
CAddrInfo& newInfo = mapInfo[id_new];
|
||||||
|
|
||||||
// which tried bucket to move the entry to
|
// which tried bucket to move the entry to
|
||||||
int tried_bucket = newInfo.GetTriedBucket(nKey);
|
int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
|
||||||
int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
|
int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
|
||||||
|
|
||||||
int id_old = vvTried[tried_bucket][tried_bucket_pos];
|
int id_old = vvTried[tried_bucket][tried_bucket_pos];
|
||||||
|
|
||||||
return mapInfo[id_old];
|
return mapInfo[id_old];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
|
||||||
|
{
|
||||||
|
std::vector<bool> bits;
|
||||||
|
FILE *filestr = fsbridge::fopen(path, "rb");
|
||||||
|
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||||
|
if (file.IsNull()) {
|
||||||
|
LogPrintf("Failed to open asmap file from disk.\n");
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
fseek(filestr, 0, SEEK_END);
|
||||||
|
int length = ftell(filestr);
|
||||||
|
LogPrintf("Opened asmap file %s (%d bytes) from disk.\n", path, length);
|
||||||
|
fseek(filestr, 0, SEEK_SET);
|
||||||
|
char cur_byte;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
file >> cur_byte;
|
||||||
|
for (int bit = 0; bit < 8; ++bit) {
|
||||||
|
bits.push_back((cur_byte >> bit) & 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
|
@ -12,11 +12,17 @@
|
||||||
#include <sync.h>
|
#include <sync.h>
|
||||||
#include <timedata.h>
|
#include <timedata.h>
|
||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
#include <clientversion.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <streams.h>
|
||||||
|
#include <fs.h>
|
||||||
|
#include <hash.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extended statistics about a CAddress
|
* Extended statistics about a CAddress
|
||||||
|
@ -72,15 +78,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Calculate in which "tried" bucket this entry belongs
|
//! Calculate in which "tried" bucket this entry belongs
|
||||||
int GetTriedBucket(const uint256 &nKey) const;
|
int GetTriedBucket(const uint256 &nKey, const std::vector<bool> &asmap) const;
|
||||||
|
|
||||||
//! Calculate in which "new" bucket this entry belongs, given a certain source
|
//! Calculate in which "new" bucket this entry belongs, given a certain source
|
||||||
int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
|
int GetNewBucket(const uint256 &nKey, const CNetAddr& src, const std::vector<bool> &asmap) const;
|
||||||
|
|
||||||
//! Calculate in which "new" bucket this entry belongs, using its default source
|
//! Calculate in which "new" bucket this entry belongs, using its default source
|
||||||
int GetNewBucket(const uint256 &nKey) const
|
int GetNewBucket(const uint256 &nKey, const std::vector<bool> &asmap) const
|
||||||
{
|
{
|
||||||
return GetNewBucket(nKey, source);
|
return GetNewBucket(nKey, source, asmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Calculate in which position of a bucket to store this entry.
|
//! Calculate in which position of a bucket to store this entry.
|
||||||
|
@ -174,6 +180,7 @@ static const int64_t ADDRMAN_TEST_WINDOW = 40*60; // 40 minutes
|
||||||
*/
|
*/
|
||||||
class CAddrMan
|
class CAddrMan
|
||||||
{
|
{
|
||||||
|
friend class CAddrManTest;
|
||||||
protected:
|
protected:
|
||||||
//! critical section to protect the inner data structures
|
//! critical section to protect the inner data structures
|
||||||
mutable CCriticalSection cs;
|
mutable CCriticalSection cs;
|
||||||
|
@ -268,9 +275,29 @@ protected:
|
||||||
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// If asmap is provided, nodes will be bucketed by
|
||||||
|
// AS they belong to, in order to make impossible for a node
|
||||||
|
// to connect to several nodes hosted in a single AS.
|
||||||
|
// This is done in response to Erebus attack, but also to generally
|
||||||
|
// diversify the connections every node creates,
|
||||||
|
// especially useful when a large fraction of nodes
|
||||||
|
// operate under a couple of cloud providers.
|
||||||
|
//
|
||||||
|
// If a new asmap was provided, the existing records
|
||||||
|
// would be re-bucketed accordingly.
|
||||||
|
std::vector<bool> m_asmap;
|
||||||
|
|
||||||
|
// Read asmap from provided binary file
|
||||||
|
static std::vector<bool> DecodeAsmap(fs::path path);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* serialized format:
|
* serialized format:
|
||||||
* * version byte (currently 1)
|
* * version byte (1 for pre-asmap files, 2 for files including asmap version)
|
||||||
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
||||||
* * nNew
|
* * nNew
|
||||||
* * nTried
|
* * nTried
|
||||||
|
@ -302,7 +329,7 @@ public:
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
||||||
unsigned char nVersion = 1;
|
unsigned char nVersion = 2;
|
||||||
s << nVersion;
|
s << nVersion;
|
||||||
s << ((unsigned char)32);
|
s << ((unsigned char)32);
|
||||||
s << nKey;
|
s << nKey;
|
||||||
|
@ -345,6 +372,13 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Store asmap version after bucket entries so that it
|
||||||
|
// can be ignored by older clients for backward compatibility.
|
||||||
|
uint256 asmap_version;
|
||||||
|
if (m_asmap.size() != 0) {
|
||||||
|
asmap_version = SerializeHash(m_asmap);
|
||||||
|
}
|
||||||
|
s << asmap_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
|
@ -353,7 +387,6 @@ public:
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
unsigned char nVersion;
|
unsigned char nVersion;
|
||||||
s >> nVersion;
|
s >> nVersion;
|
||||||
unsigned char nKeySize;
|
unsigned char nKeySize;
|
||||||
|
@ -383,16 +416,6 @@ public:
|
||||||
mapAddr[info] = n;
|
mapAddr[info] = n;
|
||||||
info.nRandomPos = vRandom.size();
|
info.nRandomPos = vRandom.size();
|
||||||
vRandom.push_back(n);
|
vRandom.push_back(n);
|
||||||
if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
|
|
||||||
// In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
|
|
||||||
// immediately try to give them a reference based on their primary source address.
|
|
||||||
int nUBucket = info.GetNewBucket(nKey);
|
|
||||||
int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
|
|
||||||
if (vvNew[nUBucket][nUBucketPos] == -1) {
|
|
||||||
vvNew[nUBucket][nUBucketPos] = n;
|
|
||||||
info.nRefCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
nIdCount = nNew;
|
nIdCount = nNew;
|
||||||
|
|
||||||
|
@ -401,7 +424,7 @@ public:
|
||||||
for (int n = 0; n < nTried; n++) {
|
for (int n = 0; n < nTried; n++) {
|
||||||
CAddrInfo info;
|
CAddrInfo info;
|
||||||
s >> info;
|
s >> info;
|
||||||
int nKBucket = info.GetTriedBucket(nKey);
|
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
|
||||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||||
if (vvTried[nKBucket][nKBucketPos] == -1) {
|
if (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||||
info.nRandomPos = vRandom.size();
|
info.nRandomPos = vRandom.size();
|
||||||
|
@ -417,7 +440,9 @@ public:
|
||||||
}
|
}
|
||||||
nTried -= nLost;
|
nTried -= nLost;
|
||||||
|
|
||||||
// Deserialize positions in the new table (if possible).
|
// 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
|
||||||
|
|
||||||
for (int bucket = 0; bucket < nUBuckets; bucket++) {
|
for (int bucket = 0; bucket < nUBuckets; bucket++) {
|
||||||
int nSize = 0;
|
int nSize = 0;
|
||||||
s >> nSize;
|
s >> nSize;
|
||||||
|
@ -425,14 +450,40 @@ public:
|
||||||
int nIndex = 0;
|
int nIndex = 0;
|
||||||
s >> nIndex;
|
s >> nIndex;
|
||||||
if (nIndex >= 0 && nIndex < nNew) {
|
if (nIndex >= 0 && nIndex < nNew) {
|
||||||
CAddrInfo &info = mapInfo[nIndex];
|
entryToBucket[nIndex] = bucket;
|
||||||
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
|
||||||
if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
|
|
||||||
info.nRefCount++;
|
|
||||||
vvNew[bucket][nUBucketPos] = nIndex;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint256 supplied_asmap_version;
|
||||||
|
if (m_asmap.size() != 0) {
|
||||||
|
supplied_asmap_version = SerializeHash(m_asmap);
|
||||||
|
}
|
||||||
|
uint256 serialized_asmap_version;
|
||||||
|
if (nVersion > 1) {
|
||||||
|
s >> serialized_asmap_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(BCLog::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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune new entries with refcount 0 (as a result of collisions).
|
// Prune new entries with refcount 0 (as a result of collisions).
|
||||||
|
|
26
src/init.cpp
26
src/init.cpp
|
@ -52,7 +52,11 @@
|
||||||
#include <util/threadnames.h>
|
#include <util/threadnames.h>
|
||||||
#include <util/translation.h>
|
#include <util/translation.h>
|
||||||
#include <util/validation.h>
|
#include <util/validation.h>
|
||||||
|
#include <util/asmap.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
|
#include <hash.h>
|
||||||
|
|
||||||
|
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
#include <walletinitinterface.h>
|
#include <walletinitinterface.h>
|
||||||
|
|
||||||
|
@ -97,6 +101,8 @@ static constexpr int DUMP_BANS_INTERVAL = 60 * 15;
|
||||||
|
|
||||||
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
|
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
|
||||||
|
|
||||||
|
static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PID file facilities.
|
* The PID file facilities.
|
||||||
*/
|
*/
|
||||||
|
@ -426,6 +432,7 @@ void SetupServerArgs()
|
||||||
gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
|
gArgs.AddArg("-peertimeout=<n>", strprintf("Specify p2p connection timeout in seconds. This option determines the amount of time a peer may be inactive before the connection to it is dropped. (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
|
||||||
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
|
gArgs.AddArg("-asmap=<file>", "Specify asn mapping used for bucketing of the peers. Path should be relative to the -datadir path.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
#ifdef USE_UPNP
|
#ifdef USE_UPNP
|
||||||
#if USE_UPNP
|
#if USE_UPNP
|
||||||
gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
|
@ -1807,6 +1814,25 @@ bool AppInitMain(NodeContext& node)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read asmap file if configured
|
||||||
|
if (gArgs.IsArgSet("-asmap")) {
|
||||||
|
std::string asmap_file = gArgs.GetArg("-asmap", "");
|
||||||
|
if (asmap_file.empty()) {
|
||||||
|
asmap_file = DEFAULT_ASMAP_FILENAME;
|
||||||
|
}
|
||||||
|
const fs::path asmap_path = GetDataDir() / asmap_file;
|
||||||
|
std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path);
|
||||||
|
if (asmap.size() == 0) {
|
||||||
|
InitError(strprintf(_("Could not find or parse specified asmap: '%s'").translated, asmap_path));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
node.connman->SetAsmap(asmap);
|
||||||
|
const uint256 asmap_version = SerializeHash(asmap);
|
||||||
|
LogPrintf("Using asmap version %s for IP bucketing.\n", asmap_version.ToString());
|
||||||
|
} else {
|
||||||
|
LogPrintf("Using /16 prefix for IP bucketing.\n");
|
||||||
|
}
|
||||||
|
|
||||||
// ********************************************************* Step 13: finished
|
// ********************************************************* Step 13: finished
|
||||||
|
|
||||||
SetRPCWarmupFinished();
|
SetRPCWarmupFinished();
|
||||||
|
|
|
@ -1758,7 +1758,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||||
// but inbound and addnode peers do not use our outbound slots. Inbound peers
|
// but inbound and addnode peers do not use our outbound slots. Inbound peers
|
||||||
// also have the added issue that they're attacker controlled and could be used
|
// also have the added issue that they're attacker controlled and could be used
|
||||||
// to prevent us from connecting to particular hosts if we used them here.
|
// to prevent us from connecting to particular hosts if we used them here.
|
||||||
setConnected.insert(pnode->addr.GetGroup());
|
setConnected.insert(pnode->addr.GetGroup(addrman.m_asmap));
|
||||||
if (pnode->m_tx_relay == nullptr) {
|
if (pnode->m_tx_relay == nullptr) {
|
||||||
nOutboundBlockRelay++;
|
nOutboundBlockRelay++;
|
||||||
} else if (!pnode->fFeeler) {
|
} else if (!pnode->fFeeler) {
|
||||||
|
@ -1806,7 +1806,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Require outbound connections, other than feelers, to be to distinct network groups
|
// Require outbound connections, other than feelers, to be to distinct network groups
|
||||||
if (!fFeeler && setConnected.count(addr.GetGroup())) {
|
if (!fFeeler && setConnected.count(addr.GetGroup(addrman.m_asmap))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2778,7 +2778,7 @@ CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
|
||||||
|
|
||||||
uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const
|
uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> vchNetGroup(ad.GetGroup());
|
std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.m_asmap));
|
||||||
|
|
||||||
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
|
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,6 +153,7 @@ public:
|
||||||
bool m_use_addrman_outgoing = true;
|
bool m_use_addrman_outgoing = true;
|
||||||
std::vector<std::string> m_specified_outgoing;
|
std::vector<std::string> m_specified_outgoing;
|
||||||
std::vector<std::string> m_added_nodes;
|
std::vector<std::string> m_added_nodes;
|
||||||
|
std::vector<bool> m_asmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Init(const Options& connOptions) {
|
void Init(const Options& connOptions) {
|
||||||
|
@ -330,6 +331,8 @@ public:
|
||||||
*/
|
*/
|
||||||
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
|
int64_t PoissonNextSendInbound(int64_t now, int average_interval_seconds);
|
||||||
|
|
||||||
|
void SetAsmap(std::vector<bool> asmap) { addrman.m_asmap = asmap; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ListenSocket {
|
struct ListenSocket {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include <netaddress.h>
|
#include <netaddress.h>
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <util/strencodings.h>
|
#include <util/strencodings.h>
|
||||||
|
#include <util/asmap.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
|
|
||||||
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
|
static const unsigned char pchIPv4[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
|
||||||
|
@ -410,7 +411,7 @@ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const
|
||||||
* @note No two connections will be attempted to addresses with the same network
|
* @note No two connections will be attempted to addresses with the same network
|
||||||
* group.
|
* group.
|
||||||
*/
|
*/
|
||||||
std::vector<unsigned char> CNetAddr::GetGroup() const
|
std::vector<unsigned char> CNetAddr::GetGroup(const std::vector<bool> &asmap) const
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> vchRet;
|
std::vector<unsigned char> vchRet;
|
||||||
int nClass = NET_IPV6;
|
int nClass = NET_IPV6;
|
||||||
|
@ -470,6 +471,27 @@ std::vector<unsigned char> CNetAddr::GetGroup() const
|
||||||
else
|
else
|
||||||
nBits = 32;
|
nBits = 32;
|
||||||
|
|
||||||
|
// If asmap is supplied and the address is IPv4/IPv6,
|
||||||
|
// ignore nBits and use 32/128 bits to obtain ASN from asmap.
|
||||||
|
// ASN is then returned to be used for bucketing.
|
||||||
|
if (asmap.size() != 0 && (nClass == NET_IPV4 || nClass == NET_IPV6)) {
|
||||||
|
nClass = NET_IPV6;
|
||||||
|
std::vector<bool> ip_bits(128);
|
||||||
|
for (int8_t byte_i = 0; byte_i < 16; ++byte_i) {
|
||||||
|
uint8_t cur_byte = GetByte(15 - byte_i);
|
||||||
|
for (uint8_t bit_i = 0; bit_i < 8; ++bit_i) {
|
||||||
|
ip_bits[byte_i * 8 + bit_i] = (cur_byte >> (7 - bit_i)) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t asn = Interpret(asmap, ip_bits);
|
||||||
|
vchRet.push_back(nClass);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
vchRet.push_back((asn >> (8 * i)) & 0xFF);
|
||||||
|
}
|
||||||
|
return vchRet;
|
||||||
|
}
|
||||||
|
|
||||||
vchRet.push_back(nClass);
|
vchRet.push_back(nClass);
|
||||||
|
|
||||||
// push our ip onto vchRet byte by byte...
|
// push our ip onto vchRet byte by byte...
|
||||||
|
|
|
@ -78,7 +78,8 @@ class CNetAddr
|
||||||
unsigned int GetByte(int n) const;
|
unsigned int GetByte(int n) const;
|
||||||
uint64_t GetHash() const;
|
uint64_t GetHash() const;
|
||||||
bool GetInAddr(struct in_addr* pipv4Addr) const;
|
bool GetInAddr(struct in_addr* pipv4Addr) const;
|
||||||
std::vector<unsigned char> GetGroup() const;
|
std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
|
||||||
|
|
||||||
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
|
int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
|
||||||
|
|
||||||
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
|
explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <test/util/setup_common.h>
|
#include <test/util/setup_common.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#include <util/asmap.h>
|
||||||
|
#include <test/data/asmap.raw.h>
|
||||||
|
|
||||||
#include <hash.h>
|
#include <hash.h>
|
||||||
#include <netbase.h>
|
#include <netbase.h>
|
||||||
|
@ -12,13 +14,18 @@
|
||||||
|
|
||||||
class CAddrManTest : public CAddrMan
|
class CAddrManTest : public CAddrMan
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
bool deterministic;
|
||||||
public:
|
public:
|
||||||
explicit CAddrManTest(bool makeDeterministic = true)
|
explicit CAddrManTest(bool makeDeterministic = true,
|
||||||
|
std::vector<bool> asmap = std::vector<bool>())
|
||||||
{
|
{
|
||||||
if (makeDeterministic) {
|
if (makeDeterministic) {
|
||||||
// Set addrman addr placement to be deterministic.
|
// Set addrman addr placement to be deterministic.
|
||||||
MakeDeterministic();
|
MakeDeterministic();
|
||||||
}
|
}
|
||||||
|
deterministic = makeDeterministic;
|
||||||
|
m_asmap = asmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Ensure that bucket placement is always the same for testing purposes.
|
//! Ensure that bucket placement is always the same for testing purposes.
|
||||||
|
@ -46,6 +53,21 @@ public:
|
||||||
CAddrMan::Delete(nId);
|
CAddrMan::Delete(nId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to test deserialization
|
||||||
|
std::pair<int, int> GetBucketAndEntry(const CAddress& addr)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
int nId = mapAddr[addr];
|
||||||
|
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
|
||||||
|
for (int entry = 0; entry < ADDRMAN_BUCKET_SIZE; ++entry) {
|
||||||
|
if (nId == vvNew[bucket][entry]) {
|
||||||
|
return std::pair<int, int>(bucket, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::pair<int, int>(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
// Simulates connection failure so that we can test eviction of offline nodes
|
// Simulates connection failure so that we can test eviction of offline nodes
|
||||||
void SimConnFail(CService& addr)
|
void SimConnFail(CService& addr)
|
||||||
{
|
{
|
||||||
|
@ -57,6 +79,16 @@ public:
|
||||||
int64_t nLastTry = GetAdjustedTime()-61;
|
int64_t nLastTry = GetAdjustedTime()-61;
|
||||||
Attempt(addr, count_failure, nLastTry);
|
Attempt(addr, count_failure, nLastTry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
CAddrMan::Clear();
|
||||||
|
if (deterministic) {
|
||||||
|
nKey.SetNull();
|
||||||
|
insecure_rand = FastRandomContext(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static CNetAddr ResolveIP(const char* ip)
|
static CNetAddr ResolveIP(const char* ip)
|
||||||
|
@ -83,6 +115,18 @@ static CService ResolveService(std::string ip, int port = 0)
|
||||||
return ResolveService(ip.c_str(), port);
|
return ResolveService(ip.c_str(), port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<bool> FromBytes(const unsigned char* source, int vector_size) {
|
||||||
|
std::vector<bool> result(vector_size);
|
||||||
|
for (int byte_i = 0; byte_i < vector_size / 8; ++byte_i) {
|
||||||
|
unsigned char cur_byte = source[byte_i];
|
||||||
|
for (int bit_i = 0; bit_i < 8; ++bit_i) {
|
||||||
|
result[byte_i * 8 + bit_i] = (cur_byte >> bit_i) & 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
|
BOOST_FIXTURE_TEST_SUITE(addrman_tests, BasicTestingSetup)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(addrman_simple)
|
BOOST_AUTO_TEST_CASE(addrman_simple)
|
||||||
|
@ -409,6 +453,139 @@ BOOST_AUTO_TEST_CASE(addrman_getaddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket_legacy)
|
||||||
|
{
|
||||||
|
CAddrManTest addrman;
|
||||||
|
|
||||||
|
CAddress addr1 = CAddress(ResolveService("250.1.1.1", 8333), NODE_NONE);
|
||||||
|
CAddress addr2 = CAddress(ResolveService("250.1.1.1", 9999), NODE_NONE);
|
||||||
|
|
||||||
|
CNetAddr source1 = ResolveIP("250.1.1.1");
|
||||||
|
|
||||||
|
|
||||||
|
CAddrInfo info1 = CAddrInfo(addr1, source1);
|
||||||
|
|
||||||
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
||||||
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
||||||
|
|
||||||
|
std::vector<bool> asmap; // use /16
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, asmap), 40);
|
||||||
|
|
||||||
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
||||||
|
// this test could be a security issue.
|
||||||
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap));
|
||||||
|
|
||||||
|
// Test: Two addresses with same IP but different ports can map to
|
||||||
|
// different buckets because they have different keys.
|
||||||
|
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
||||||
|
|
||||||
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
||||||
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap));
|
||||||
|
|
||||||
|
std::set<int> buckets;
|
||||||
|
for (int i = 0; i < 255; i++) {
|
||||||
|
CAddrInfo infoi = CAddrInfo(
|
||||||
|
CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
|
||||||
|
ResolveIP("250.1.1." + std::to_string(i)));
|
||||||
|
int bucket = infoi.GetTriedBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the same /16 prefix should
|
||||||
|
// never get more than 8 buckets with legacy grouping
|
||||||
|
BOOST_CHECK_EQUAL(buckets.size(), 8U);
|
||||||
|
|
||||||
|
buckets.clear();
|
||||||
|
for (int j = 0; j < 255; j++) {
|
||||||
|
CAddrInfo infoj = CAddrInfo(
|
||||||
|
CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE),
|
||||||
|
ResolveIP("250." + std::to_string(j) + ".1.1"));
|
||||||
|
int bucket = infoj.GetTriedBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the different /16 prefix should map to more than
|
||||||
|
// 8 buckets with legacy grouping
|
||||||
|
BOOST_CHECK_EQUAL(buckets.size(), 160U);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket_legacy)
|
||||||
|
{
|
||||||
|
CAddrManTest addrman;
|
||||||
|
|
||||||
|
CAddress addr1 = CAddress(ResolveService("250.1.2.1", 8333), NODE_NONE);
|
||||||
|
CAddress addr2 = CAddress(ResolveService("250.1.2.1", 9999), NODE_NONE);
|
||||||
|
|
||||||
|
CNetAddr source1 = ResolveIP("250.1.2.1");
|
||||||
|
|
||||||
|
CAddrInfo info1 = CAddrInfo(addr1, source1);
|
||||||
|
|
||||||
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
||||||
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
||||||
|
|
||||||
|
std::vector<bool> asmap; // use /16
|
||||||
|
|
||||||
|
// Test: Make sure the buckets are what we expect
|
||||||
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), 786);
|
||||||
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, asmap), 786);
|
||||||
|
|
||||||
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
||||||
|
// this test could be a security issue.
|
||||||
|
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap));
|
||||||
|
|
||||||
|
// Test: Ports should not affect bucket placement in the addr
|
||||||
|
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
||||||
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
||||||
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap));
|
||||||
|
|
||||||
|
std::set<int> buckets;
|
||||||
|
for (int i = 0; i < 255; i++) {
|
||||||
|
CAddrInfo infoi = CAddrInfo(
|
||||||
|
CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
|
||||||
|
ResolveIP("250.1.1." + std::to_string(i)));
|
||||||
|
int bucket = infoi.GetNewBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the same group (\16 prefix for IPv4) should
|
||||||
|
// always map to the same bucket.
|
||||||
|
BOOST_CHECK_EQUAL(buckets.size(), 1U);
|
||||||
|
|
||||||
|
buckets.clear();
|
||||||
|
for (int j = 0; j < 4 * 255; j++) {
|
||||||
|
CAddrInfo infoj = CAddrInfo(CAddress(
|
||||||
|
ResolveService(
|
||||||
|
std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE),
|
||||||
|
ResolveIP("251.4.1.1"));
|
||||||
|
int bucket = infoj.GetNewBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the same source groups should map to NO MORE
|
||||||
|
// than 64 buckets.
|
||||||
|
BOOST_CHECK(buckets.size() <= 64);
|
||||||
|
|
||||||
|
buckets.clear();
|
||||||
|
for (int p = 0; p < 255; p++) {
|
||||||
|
CAddrInfo infoj = CAddrInfo(
|
||||||
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
||||||
|
ResolveIP("250." + std::to_string(p) + ".1.1"));
|
||||||
|
int bucket = infoj.GetNewBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the different source groups should map to MORE
|
||||||
|
// than 64 buckets.
|
||||||
|
BOOST_CHECK(buckets.size() > 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following three test cases use asmap.raw
|
||||||
|
// We use an artificial minimal mock mapping
|
||||||
|
// 250.0.0.0/8 AS1000
|
||||||
|
// 101.1.0.0/16 AS1
|
||||||
|
// 101.2.0.0/16 AS2
|
||||||
|
// 101.3.0.0/16 AS3
|
||||||
|
// 101.4.0.0/16 AS4
|
||||||
|
// 101.5.0.0/16 AS5
|
||||||
|
// 101.6.0.0/16 AS6
|
||||||
|
// 101.7.0.0/16 AS7
|
||||||
|
// 101.8.0.0/16 AS8
|
||||||
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
|
||||||
{
|
{
|
||||||
CAddrManTest addrman;
|
CAddrManTest addrman;
|
||||||
|
@ -424,43 +601,44 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_tried_bucket)
|
||||||
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
||||||
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
||||||
|
|
||||||
|
std::vector<bool> asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1), 40);
|
BOOST_CHECK_EQUAL(info1.GetTriedBucket(nKey1, asmap), 236);
|
||||||
|
|
||||||
// Test: Make sure key actually randomizes bucket placement. A fail on
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
||||||
// this test could be a security issue.
|
// this test could be a security issue.
|
||||||
BOOST_CHECK(info1.GetTriedBucket(nKey1) != info1.GetTriedBucket(nKey2));
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info1.GetTriedBucket(nKey2, asmap));
|
||||||
|
|
||||||
// Test: Two addresses with same IP but different ports can map to
|
// Test: Two addresses with same IP but different ports can map to
|
||||||
// different buckets because they have different keys.
|
// different buckets because they have different keys.
|
||||||
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
||||||
|
|
||||||
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
||||||
BOOST_CHECK(info1.GetTriedBucket(nKey1) != info2.GetTriedBucket(nKey1));
|
BOOST_CHECK(info1.GetTriedBucket(nKey1, asmap) != info2.GetTriedBucket(nKey1, asmap));
|
||||||
|
|
||||||
std::set<int> buckets;
|
std::set<int> buckets;
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int j = 0; j < 255; j++) {
|
||||||
CAddrInfo infoi = CAddrInfo(
|
CAddrInfo infoj = CAddrInfo(
|
||||||
CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
|
CAddress(ResolveService("101." + std::to_string(j) + ".1.1"), NODE_NONE),
|
||||||
ResolveIP("250.1.1." + std::to_string(i)));
|
ResolveIP("101." + std::to_string(j) + ".1.1"));
|
||||||
int bucket = infoi.GetTriedBucket(nKey1);
|
int bucket = infoj.GetTriedBucket(nKey1, asmap);
|
||||||
buckets.insert(bucket);
|
buckets.insert(bucket);
|
||||||
}
|
}
|
||||||
// Test: IP addresses in the same group (\16 prefix for IPv4) should
|
// Test: IP addresses in the different /16 prefix MAY map to more than
|
||||||
// never get more than 8 buckets
|
// 8 buckets.
|
||||||
BOOST_CHECK_EQUAL(buckets.size(), 8U);
|
BOOST_CHECK(buckets.size() > 8);
|
||||||
|
|
||||||
buckets.clear();
|
buckets.clear();
|
||||||
for (int j = 0; j < 255; j++) {
|
for (int j = 0; j < 255; j++) {
|
||||||
CAddrInfo infoj = CAddrInfo(
|
CAddrInfo infoj = CAddrInfo(
|
||||||
CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE),
|
CAddress(ResolveService("250." + std::to_string(j) + ".1.1"), NODE_NONE),
|
||||||
ResolveIP("250." + std::to_string(j) + ".1.1"));
|
ResolveIP("250." + std::to_string(j) + ".1.1"));
|
||||||
int bucket = infoj.GetTriedBucket(nKey1);
|
int bucket = infoj.GetTriedBucket(nKey1, asmap);
|
||||||
buckets.insert(bucket);
|
buckets.insert(bucket);
|
||||||
}
|
}
|
||||||
// Test: IP addresses in the different groups should map to more than
|
// Test: IP addresses in the different /16 prefix MAY NOT map to more than
|
||||||
// 8 buckets.
|
// 8 buckets.
|
||||||
BOOST_CHECK_EQUAL(buckets.size(), 160U);
|
BOOST_CHECK(buckets.size() == 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
||||||
|
@ -477,29 +655,31 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
||||||
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
uint256 nKey1 = (uint256)(CHashWriter(SER_GETHASH, 0) << 1).GetHash();
|
||||||
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
uint256 nKey2 = (uint256)(CHashWriter(SER_GETHASH, 0) << 2).GetHash();
|
||||||
|
|
||||||
|
std::vector<bool> asmap = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
||||||
|
|
||||||
// Test: Make sure the buckets are what we expect
|
// Test: Make sure the buckets are what we expect
|
||||||
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1), 786);
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), 795);
|
||||||
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1), 786);
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, source1, asmap), 795);
|
||||||
|
|
||||||
// Test: Make sure key actually randomizes bucket placement. A fail on
|
// Test: Make sure key actually randomizes bucket placement. A fail on
|
||||||
// this test could be a security issue.
|
// this test could be a security issue.
|
||||||
BOOST_CHECK(info1.GetNewBucket(nKey1) != info1.GetNewBucket(nKey2));
|
BOOST_CHECK(info1.GetNewBucket(nKey1, asmap) != info1.GetNewBucket(nKey2, asmap));
|
||||||
|
|
||||||
// Test: Ports should not affect bucket placement in the addr
|
// Test: Ports should not affect bucket placement in the addr
|
||||||
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
CAddrInfo info2 = CAddrInfo(addr2, source1);
|
||||||
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
BOOST_CHECK(info1.GetKey() != info2.GetKey());
|
||||||
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1), info2.GetNewBucket(nKey1));
|
BOOST_CHECK_EQUAL(info1.GetNewBucket(nKey1, asmap), info2.GetNewBucket(nKey1, asmap));
|
||||||
|
|
||||||
std::set<int> buckets;
|
std::set<int> buckets;
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < 255; i++) {
|
||||||
CAddrInfo infoi = CAddrInfo(
|
CAddrInfo infoi = CAddrInfo(
|
||||||
CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
|
CAddress(ResolveService("250.1.1." + std::to_string(i)), NODE_NONE),
|
||||||
ResolveIP("250.1.1." + std::to_string(i)));
|
ResolveIP("250.1.1." + std::to_string(i)));
|
||||||
int bucket = infoi.GetNewBucket(nKey1);
|
int bucket = infoi.GetNewBucket(nKey1, asmap);
|
||||||
buckets.insert(bucket);
|
buckets.insert(bucket);
|
||||||
}
|
}
|
||||||
// Test: IP addresses in the same group (\16 prefix for IPv4) should
|
// Test: IP addresses in the same /16 prefix
|
||||||
// always map to the same bucket.
|
// usually map to the same bucket.
|
||||||
BOOST_CHECK_EQUAL(buckets.size(), 1U);
|
BOOST_CHECK_EQUAL(buckets.size(), 1U);
|
||||||
|
|
||||||
buckets.clear();
|
buckets.clear();
|
||||||
|
@ -508,10 +688,10 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
||||||
ResolveService(
|
ResolveService(
|
||||||
std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE),
|
std::to_string(250 + (j / 255)) + "." + std::to_string(j % 256) + ".1.1"), NODE_NONE),
|
||||||
ResolveIP("251.4.1.1"));
|
ResolveIP("251.4.1.1"));
|
||||||
int bucket = infoj.GetNewBucket(nKey1);
|
int bucket = infoj.GetNewBucket(nKey1, asmap);
|
||||||
buckets.insert(bucket);
|
buckets.insert(bucket);
|
||||||
}
|
}
|
||||||
// Test: IP addresses in the same source groups should map to no more
|
// Test: IP addresses in the same source /16 prefix should not map to more
|
||||||
// than 64 buckets.
|
// than 64 buckets.
|
||||||
BOOST_CHECK(buckets.size() <= 64);
|
BOOST_CHECK(buckets.size() <= 64);
|
||||||
|
|
||||||
|
@ -519,13 +699,92 @@ BOOST_AUTO_TEST_CASE(caddrinfo_get_new_bucket)
|
||||||
for (int p = 0; p < 255; p++) {
|
for (int p = 0; p < 255; p++) {
|
||||||
CAddrInfo infoj = CAddrInfo(
|
CAddrInfo infoj = CAddrInfo(
|
||||||
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
||||||
ResolveIP("250." + std::to_string(p) + ".1.1"));
|
ResolveIP("101." + std::to_string(p) + ".1.1"));
|
||||||
int bucket = infoj.GetNewBucket(nKey1);
|
int bucket = infoj.GetNewBucket(nKey1, asmap);
|
||||||
buckets.insert(bucket);
|
buckets.insert(bucket);
|
||||||
}
|
}
|
||||||
// Test: IP addresses in the different source groups should map to more
|
// Test: IP addresses in the different source /16 prefixes usually map to MORE
|
||||||
// than 64 buckets.
|
// than 1 bucket.
|
||||||
BOOST_CHECK(buckets.size() > 64);
|
BOOST_CHECK(buckets.size() > 1);
|
||||||
|
|
||||||
|
buckets.clear();
|
||||||
|
for (int p = 0; p < 255; p++) {
|
||||||
|
CAddrInfo infoj = CAddrInfo(
|
||||||
|
CAddress(ResolveService("250.1.1.1"), NODE_NONE),
|
||||||
|
ResolveIP("250." + std::to_string(p) + ".1.1"));
|
||||||
|
int bucket = infoj.GetNewBucket(nKey1, asmap);
|
||||||
|
buckets.insert(bucket);
|
||||||
|
}
|
||||||
|
// Test: IP addresses in the different source /16 prefixes sometimes map to NO MORE
|
||||||
|
// than 1 bucket.
|
||||||
|
BOOST_CHECK(buckets.size() == 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(addrman_serialization)
|
||||||
|
{
|
||||||
|
std::vector<bool> asmap1 = FromBytes(asmap_raw, sizeof(asmap_raw) * 8);
|
||||||
|
|
||||||
|
CAddrManTest addrman_asmap1(true, asmap1);
|
||||||
|
CAddrManTest addrman_asmap1_dup(true, asmap1);
|
||||||
|
CAddrManTest addrman_noasmap;
|
||||||
|
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
|
||||||
|
CAddress addr = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
|
||||||
|
CNetAddr default_source;
|
||||||
|
|
||||||
|
|
||||||
|
addrman_asmap1.Add(addr, default_source);
|
||||||
|
|
||||||
|
stream << addrman_asmap1;
|
||||||
|
// serizalizing/deserializing addrman with the same asmap
|
||||||
|
stream >> addrman_asmap1_dup;
|
||||||
|
|
||||||
|
std::pair<int, int> bucketAndEntry_asmap1 = addrman_asmap1.GetBucketAndEntry(addr);
|
||||||
|
std::pair<int, int> bucketAndEntry_asmap1_dup = addrman_asmap1_dup.GetBucketAndEntry(addr);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1.second != -1);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_dup.second != -1);
|
||||||
|
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1.first == bucketAndEntry_asmap1_dup.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1.second == bucketAndEntry_asmap1_dup.second);
|
||||||
|
|
||||||
|
// deserializing asmaped peers.dat to non-asmaped addrman
|
||||||
|
stream << addrman_asmap1;
|
||||||
|
stream >> addrman_noasmap;
|
||||||
|
std::pair<int, int> bucketAndEntry_noasmap = addrman_noasmap.GetBucketAndEntry(addr);
|
||||||
|
BOOST_CHECK(bucketAndEntry_noasmap.second != -1);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1.first != bucketAndEntry_noasmap.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1.second != bucketAndEntry_noasmap.second);
|
||||||
|
|
||||||
|
// deserializing non-asmaped peers.dat to asmaped addrman
|
||||||
|
addrman_asmap1.Clear();
|
||||||
|
addrman_noasmap.Clear();
|
||||||
|
addrman_noasmap.Add(addr, default_source);
|
||||||
|
stream << addrman_noasmap;
|
||||||
|
stream >> addrman_asmap1;
|
||||||
|
std::pair<int, int> bucketAndEntry_asmap1_deser = addrman_asmap1.GetBucketAndEntry(addr);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser.second != -1);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser.first != bucketAndEntry_noasmap.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser.first == bucketAndEntry_asmap1_dup.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser.second == bucketAndEntry_asmap1_dup.second);
|
||||||
|
|
||||||
|
// used to map to different buckets, now maps to the same bucket.
|
||||||
|
addrman_asmap1.Clear();
|
||||||
|
addrman_noasmap.Clear();
|
||||||
|
CAddress addr1 = CAddress(ResolveService("250.1.1.1"), NODE_NONE);
|
||||||
|
CAddress addr2 = CAddress(ResolveService("250.2.1.1"), NODE_NONE);
|
||||||
|
addrman_noasmap.Add(addr, default_source);
|
||||||
|
addrman_noasmap.Add(addr2, default_source);
|
||||||
|
std::pair<int, int> bucketAndEntry_noasmap_addr1 = addrman_noasmap.GetBucketAndEntry(addr1);
|
||||||
|
std::pair<int, int> bucketAndEntry_noasmap_addr2 = addrman_noasmap.GetBucketAndEntry(addr2);
|
||||||
|
BOOST_CHECK(bucketAndEntry_noasmap_addr1.first != bucketAndEntry_noasmap_addr2.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_noasmap_addr1.second != bucketAndEntry_noasmap_addr2.second);
|
||||||
|
stream << addrman_noasmap;
|
||||||
|
stream >> addrman_asmap1;
|
||||||
|
std::pair<int, int> bucketAndEntry_asmap1_deser_addr1 = addrman_asmap1.GetBucketAndEntry(addr1);
|
||||||
|
std::pair<int, int> bucketAndEntry_asmap1_deser_addr2 = addrman_asmap1.GetBucketAndEntry(addr2);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.first == bucketAndEntry_asmap1_deser_addr2.first);
|
||||||
|
BOOST_CHECK(bucketAndEntry_asmap1_deser_addr1.second != bucketAndEntry_asmap1_deser_addr2.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
BIN
src/test/data/asmap.raw
Normal file
BIN
src/test/data/asmap.raw
Normal file
Binary file not shown.
|
@ -285,23 +285,23 @@ BOOST_AUTO_TEST_CASE(subnet_test)
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(netbase_getgroup)
|
BOOST_AUTO_TEST_CASE(netbase_getgroup)
|
||||||
{
|
{
|
||||||
|
std::vector<bool> asmap; // use /16
|
||||||
BOOST_CHECK(ResolveIP("127.0.0.1").GetGroup() == std::vector<unsigned char>({0})); // Local -> !Routable()
|
BOOST_CHECK(ResolveIP("127.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // Local -> !Routable()
|
||||||
BOOST_CHECK(ResolveIP("257.0.0.1").GetGroup() == std::vector<unsigned char>({0})); // !Valid -> !Routable()
|
BOOST_CHECK(ResolveIP("257.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // !Valid -> !Routable()
|
||||||
BOOST_CHECK(ResolveIP("10.0.0.1").GetGroup() == std::vector<unsigned char>({0})); // RFC1918 -> !Routable()
|
BOOST_CHECK(ResolveIP("10.0.0.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // RFC1918 -> !Routable()
|
||||||
BOOST_CHECK(ResolveIP("169.254.1.1").GetGroup() == std::vector<unsigned char>({0})); // RFC3927 -> !Routable()
|
BOOST_CHECK(ResolveIP("169.254.1.1").GetGroup(asmap) == std::vector<unsigned char>({0})); // RFC3927 -> !Routable()
|
||||||
BOOST_CHECK(ResolveIP("1.2.3.4").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // IPv4
|
BOOST_CHECK(ResolveIP("1.2.3.4").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // IPv4
|
||||||
BOOST_CHECK(ResolveIP("::FFFF:0:102:304").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6145
|
BOOST_CHECK(ResolveIP("::FFFF:0:102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6145
|
||||||
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
|
BOOST_CHECK(ResolveIP("64:FF9B::102:304").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC6052
|
||||||
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
|
BOOST_CHECK(ResolveIP("2002:102:304:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC3964
|
||||||
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
|
BOOST_CHECK(ResolveIP("2001:0:9999:9999:9999:9999:FEFD:FCFB").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV4, 1, 2})); // RFC4380
|
||||||
BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor
|
BOOST_CHECK(ResolveIP("FD87:D87E:EB43:edb1:8e4:3588:e546:35ca").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_ONION, 239})); // Tor
|
||||||
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
|
BOOST_CHECK(ResolveIP("2001:470:abcd:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 4, 112, 175})); //he.net
|
||||||
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup() == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
|
BOOST_CHECK(ResolveIP("2001:2001:9999:9999:9999:9999:9999:9999").GetGroup(asmap) == std::vector<unsigned char>({(unsigned char)NET_IPV6, 32, 1, 32, 1})); //IPv6
|
||||||
|
|
||||||
// baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505
|
// baz.net sha256 hash: 12929400eb4607c4ac075f087167e75286b179c693eb059a01774b864e8fe505
|
||||||
std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07};
|
std::vector<unsigned char> internal_group = {NET_INTERNAL, 0x12, 0x92, 0x94, 0x00, 0xeb, 0x46, 0x07, 0xc4, 0xac, 0x07};
|
||||||
BOOST_CHECK(CreateInternal("baz.net").GetGroup() == internal_group);
|
BOOST_CHECK(CreateInternal("baz.net").GetGroup(asmap) == internal_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
|
BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
|
||||||
|
|
Loading…
Reference in a new issue