@ -79,17 +79,20 @@ public:
}
}
//! Calculate in which "tried" bucket this entry belongs
//! Calculate in which "tried" bucket this entry belongs
int GetTriedBucket ( const std : : vector < unsigned char > & nKey ) const ;
int GetTriedBucket ( const uint256 & nKey ) 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 std : : vector < unsigned char > & nKey , const CNetAddr & src ) const ;
int GetNewBucket ( const uint256 & nKey , const CNetAddr & src ) 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 std : : vector < unsigned char > & nKey ) const
int GetNewBucket ( const uint256 & nKey ) const
{
{
return GetNewBucket ( nKey , source ) ;
return GetNewBucket ( nKey , source ) ;
}
}
//! Calculate in which position of a bucket to store this entry.
int GetBucketPosition ( const uint256 & nKey , bool fNew , int nBucket ) const ;
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
//! Determine whether the statistics about this entry are bad enough so that it can just be deleted
bool IsTerrible ( int64_t nNow = GetAdjustedTime ( ) ) const ;
bool IsTerrible ( int64_t nNow = GetAdjustedTime ( ) ) const ;
@ -106,15 +109,15 @@ public:
*
*
* To that end :
* To that end :
* * Addresses are organized into buckets .
* * Addresses are organized into buckets .
* * Address that have not yet been tried go into 256 " new " buckets .
* * Address that have not yet been tried go into 1024 " new " buckets .
* * Based on the address range ( / 16 for IPv4 ) of source of the information , 32 buckets are selected at random
* * Based on the address range ( / 16 for IPv4 ) of source of the information , 64 buckets are selected at random
* * The actual bucket is chosen from one of these , based on the range the address itself is located .
* * The actual bucket is chosen from one of these , based on the range the address itself is located .
* * One single address can occur in up to 4 different buckets , to increase selection chances for addresses that
* * One single address can occur in up to 8 different buckets , to increase selection chances for addresses that
* are seen frequently . The chance for increasing this multiplicity decreases exponentially .
* are seen frequently . The chance for increasing this multiplicity decreases exponentially .
* * When adding a new address to a full bucket , a randomly chosen entry ( with a bias favoring less recently seen
* * When adding a new address to a full bucket , a randomly chosen entry ( with a bias favoring less recently seen
* ones ) is removed from it first .
* ones ) is removed from it first .
* * Addresses of nodes that are known to be accessible go into 64 " tried " buckets .
* * Addresses of nodes that are known to be accessible go into 25 6 " tried " buckets .
* * Each address range selects at random 4 of these buckets .
* * Each address range selects at random 8 of these buckets .
* * The actual bucket is chosen from one of these , based on the full address .
* * The actual bucket is chosen from one of these , based on the full address .
* * When adding a new good address to a full bucket , a randomly chosen entry ( with a bias favoring less recently
* * When adding a new good address to a full bucket , a randomly chosen entry ( with a bias favoring less recently
* tried ones ) is evicted from it , back to the " new " buckets .
* tried ones ) is evicted from it , back to the " new " buckets .
@ -125,28 +128,22 @@ public:
*/
*/
//! total number of buckets for tried addresses
//! total number of buckets for tried addresses
# define ADDRMAN_TRIED_BUCKET_COUNT 64
# define ADDRMAN_TRIED_BUCKET_COUNT 256
//! maximum allowed number of entries in buckets for tried addresses
# define ADDRMAN_TRIED_BUCKET_SIZE 64
//! total number of buckets for new addresses
//! total number of buckets for new addresses
# define ADDRMAN_NEW_BUCKET_COUNT 256
# define ADDRMAN_NEW_BUCKET_COUNT 1024
//! maximum allowed number of entries in buckets for new addresses
//! maximum allowed number of entries in buckets for new and tried a ddresses
# define ADDRMAN_NEW_ BUCKET_SIZE 64
# define ADDRMAN_BUCKET_SIZE 64
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
//! over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
# define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
# define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
//! over how many buckets entries with new addresses originating from a single group are spread
//! over how many buckets entries with new addresses originating from a single group are spread
# define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
# define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
//! in how many buckets for entries with new addresses a single address may occur
//! in how many buckets for entries with new addresses a single address may occur
# define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
# define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
//! how many entries in a bucket with tried addresses are inspected, when selecting one to replace
# define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
//! how old addresses can maximally be
//! how old addresses can maximally be
# define ADDRMAN_HORIZON_DAYS 30
# define ADDRMAN_HORIZON_DAYS 30
@ -176,7 +173,7 @@ private:
mutable CCriticalSection cs ;
mutable CCriticalSection cs ;
//! secret key to randomize bucket select with
//! secret key to randomize bucket select with
std : : vector < unsigned char > nKey ;
uint256 nKey ;
//! last used nId
//! last used nId
int nIdCount ;
int nIdCount ;
@ -194,13 +191,13 @@ private:
int nTried ;
int nTried ;
//! list of "tried" buckets
//! list of "tried" buckets
std : : vector < std : : vector < int > > vvTried ;
int vvTried [ ADDRMAN_TRIED_BUCKET_COUNT ] [ ADDRMAN_BUCKET_SIZE ] ;
//! number of (unique) "new" entries
//! number of (unique) "new" entries
int nNew ;
int nNew ;
//! list of "new" buckets
//! list of "new" buckets
std : : vector < std : : set < int > > vvNew ;
int vvNew [ ADDRMAN_NEW_BUCKET_COUNT ] [ ADDRMAN_BUCKET_SIZE ] ;
protected :
protected :
@ -214,17 +211,14 @@ protected:
//! Swap two elements in vRandom.
//! Swap two elements in vRandom.
void SwapRandom ( unsigned int nRandomPos1 , unsigned int nRandomPos2 ) ;
void SwapRandom ( unsigned int nRandomPos1 , unsigned int nRandomPos2 ) ;
//! Return position in given bucket to replace.
//! Move an entry from the "new" table(s) to the "tried" table
int SelectTried ( int nKBucket ) ;
void MakeTried ( CAddrInfo & info , int nId ) ;
//! Remove an element from a "new" bucket.
//! Delete an entry. It must not be in tried, and have refcount 0.
//! This is the only place where actual deletions occur.
void Delete ( int nId ) ;
//! Elements are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
int ShrinkNew ( int nUBucket ) ;
//! Move an entry from the "new" table(s) to the "tried" table
//! Clear a position in a "new" table. This is the only place where entries are actually deleted.
//! @pre vvUnkown[nOrigin].count(nId) != 0
void ClearNew ( int nUBucket , int nUBucketPos ) ;
void MakeTried ( CAddrInfo & info , int nId , int nOrigin ) ;
//! Mark an entry "good", possibly moving it from "new" to "tried".
//! Mark an entry "good", possibly moving it from "new" to "tried".
void Good_ ( const CService & addr , int64_t nTime ) ;
void Good_ ( const CService & addr , int64_t nTime ) ;
@ -237,7 +231,7 @@ protected:
//! Select an address to connect to.
//! Select an address to connect to.
//! nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
//! nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
CAddress Select_ ( int nUnkBias ) ;
CAddress Select_ ( ) ;
# ifdef DEBUG_ADDRMAN
# ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
//! Perform consistency check. Returns an error code or zero.
@ -253,17 +247,21 @@ protected:
public :
public :
/**
/**
* serialized format :
* serialized format :
* * version byte ( currently 0 )
* * version byte ( currently 1 )
* * nKey
* * 0x20 + nKey ( serialized as if it were a vector , for backward compatibility )
* * nNew
* * nNew
* * nTried
* * nTried
* * number of " new " buckets
* * number of " new " buckets XOR 2 * * 30
* * all nNew addrinfos in vvNew
* * all nNew addrinfos in vvNew
* * all nTried addrinfos in vvTried
* * all nTried addrinfos in vvTried
* * for each bucket :
* * for each bucket :
* * number of elements
* * number of elements
* * for each element : index
* * for each element : index
*
*
* 2 * * 30 is xorred with the number of buckets to make addrman deserializer v0 detect it
* as incompatible . This is necessary because it did not check the version number on
* deserialization .
*
* Notice that vvTried , mapAddr and vVector are never encoded explicitly ;
* Notice that vvTried , mapAddr and vVector are never encoded explicitly ;
* they are instead reconstructed from the other information .
* they are instead reconstructed from the other information .
*
*
@ -275,48 +273,53 @@ public:
*
*
* We don ' t use ADD_SERIALIZE_METHODS since the serialization and deserialization code has
* We don ' t use ADD_SERIALIZE_METHODS since the serialization and deserialization code has
* very little in common .
* very little in common .
*
*/
*/
template < typename Stream >
template < typename Stream >
void Serialize ( Stream & s , int nType , int nVersionDummy ) const
void Serialize ( Stream & s , int nType , int nVersionDummy ) const
{
{
LOCK ( cs ) ;
LOCK ( cs ) ;
unsigned char nVersion = 0 ;
unsigned char nVersion = 1 ;
s < < nVersion ;
s < < nVersion ;
s < < ( ( unsigned char ) 32 ) ;
s < < nKey ;
s < < nKey ;
s < < nNew ;
s < < nNew ;
s < < nTried ;
s < < nTried ;
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ;
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ ( 1 < < 30 ) ;
s < < nUBuckets ;
s < < nUBuckets ;
std : : map < int , int > mapUnkIds ;
std : : map < int , int > mapUnkIds ;
int nIds = 0 ;
int nIds = 0 ;
for ( std : : map < int , CAddrInfo > : : const_iterator it = mapInfo . begin ( ) ; it ! = mapInfo . end ( ) ; it + + ) {
for ( std : : map < int , CAddrInfo > : : const_iterator it = mapInfo . begin ( ) ; it ! = mapInfo . end ( ) ; it + + ) {
if ( nIds = = nNew ) break ; // this means nNew was wrong, oh ow
mapUnkIds [ ( * it ) . first ] = nIds ;
mapUnkIds [ ( * it ) . first ] = nIds ;
const CAddrInfo & info = ( * it ) . second ;
const CAddrInfo & info = ( * it ) . second ;
if ( info . nRefCount ) {
if ( info . nRefCount ) {
assert ( nIds ! = nNew ) ; // this means nNew was wrong, oh ow
s < < info ;
s < < info ;
nIds + + ;
nIds + + ;
}
}
}
}
nIds = 0 ;
nIds = 0 ;
for ( std : : map < int , CAddrInfo > : : const_iterator it = mapInfo . begin ( ) ; it ! = mapInfo . end ( ) ; it + + ) {
for ( std : : map < int , CAddrInfo > : : const_iterator it = mapInfo . begin ( ) ; it ! = mapInfo . end ( ) ; it + + ) {
if ( nIds = = nTried ) break ; // this means nTried was wrong, oh ow
const CAddrInfo & info = ( * it ) . second ;
const CAddrInfo & info = ( * it ) . second ;
if ( info . fInTried ) {
if ( info . fInTried ) {
assert ( nIds ! = nTried ) ; // this means nTried was wrong, oh ow
s < < info ;
s < < info ;
nIds + + ;
nIds + + ;
}
}
}
}
for ( std : : vector < std : : set < int > > : : const_iterator it = vvNew . begin ( ) ; it ! = vvNew . end ( ) ; it + + ) {
for ( int bucket = 0 ; bucket < ADDRMAN_NEW_BUCKET_COUNT ; bucket + + ) {
const std : : set < int > & vNew = ( * it ) ;
int nSize = 0 ;
int nSize = vNew . size ( ) ;
for ( int i = 0 ; i < ADDRMAN_BUCKET_SIZE ; i + + ) {
if ( vvNew [ bucket ] [ i ] ! = - 1 )
nSize + + ;
}
s < < nSize ;
s < < nSize ;
for ( std : : set < int > : : const_iterator it2 = vNew . begin ( ) ; it2 ! = vNew . end ( ) ; it2 + + ) {
for ( int i = 0 ; i < ADDRMAN_BUCKET_SIZE ; i + + ) {
int nIndex = mapUnkIds [ * it2 ] ;
if ( vvNew [ bucket ] [ i ] ! = - 1 ) {
s < < nIndex ;
int nIndex = mapUnkIds [ vvNew [ bucket ] [ i ] ] ;
s < < nIndex ;
}
}
}
}
}
}
}
@ -326,64 +329,97 @@ public:
{
{
LOCK ( cs ) ;
LOCK ( cs ) ;
Clear ( ) ;
unsigned char nVersion ;
unsigned char nVersion ;
s > > nVersion ;
s > > nVersion ;
unsigned char nKeySize ;
s > > nKeySize ;
if ( nKeySize ! = 32 ) throw std : : ios_base : : failure ( " Incorrect keysize in addrman deserialization " ) ;
s > > nKey ;
s > > nKey ;
s > > nNew ;
s > > nNew ;
s > > nTried ;
s > > nTried ;
int nUBuckets = 0 ;
int nUBuckets = 0 ;
s > > nUBuckets ;
s > > nUBuckets ;
nIdCount = 0 ;
if ( nVersion ! = 0 ) {
mapInfo . clear ( ) ;
nUBuckets ^ = ( 1 < < 30 ) ;
mapAddr . clear ( ) ;
}
vRandom . clear ( ) ;
vvTried = std : : vector < std : : vector < int > > ( ADDRMAN_TRIED_BUCKET_COUNT , std : : vector < int > ( 0 ) ) ;
// Deserialize entries from the new table.
vvNew = std : : vector < std : : set < int > > ( ADDRMAN_NEW_BUCKET_COUNT , std : : set < int > ( ) ) ;
for ( int n = 0 ; n < nNew ; n + + ) {
for ( int n = 0 ; n < nNew ; n + + ) {
CAddrInfo & info = mapInfo [ n ] ;
CAddrInfo & info = mapInfo [ n ] ;
s > > info ;
s > > info ;
mapAddr [ info ] = n ;
mapAddr [ info ] = n ;
info . nRandomPos = vRandom . size ( ) ;
info . nRandomPos = vRandom . size ( ) ;
vRandom . push_back ( n ) ;
vRandom . push_back ( n ) ;
if ( nUBuckets ! = ADDRMAN_NEW_BUCKET_COUNT ) {
if ( nVersion ! = 1 | | nUBuckets ! = ADDRMAN_NEW_BUCKET_COUNT ) {
vvNew [ info . GetNewBucket ( nKey ) ] . insert ( n ) ;
// In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
info . nRefCount + + ;
// 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 ;
// Deserialize entries from the tried table.
int nLost = 0 ;
int nLost = 0 ;
for ( int n = 0 ; n < nTried ; n + + ) {
for ( int n = 0 ; n < nTried ; n + + ) {
CAddrInfo info ;
CAddrInfo info ;
s > > info ;
s > > info ;
std : : vector < int > & vTried = vvTried [ info . GetTriedBucket ( nKey ) ] ;
int nKBucket = info . GetTriedBucket ( nKey ) ;
if ( vTried . size ( ) < ADDRMAN_TRIED_BUCKET_SIZE ) {
int nKBucketPos = info . GetBucketPosition ( nKey , false , nKBucket ) ;
if ( vvTried [ nKBucket ] [ nKBucketPos ] = = - 1 ) {
info . nRandomPos = vRandom . size ( ) ;
info . nRandomPos = vRandom . size ( ) ;
info . fInTried = true ;
info . fInTried = true ;
vRandom . push_back ( nIdCount ) ;
vRandom . push_back ( nIdCount ) ;
mapInfo [ nIdCount ] = info ;
mapInfo [ nIdCount ] = info ;
mapAddr [ info ] = nIdCount ;
mapAddr [ info ] = nIdCount ;
vTried . push_back ( nIdCount ) ;
vvTried [ nKBucket ] [ nKBucketPos ] = nIdCount ;
nIdCount + + ;
nIdCount + + ;
} else {
} else {
nLost + + ;
nLost + + ;
}
}
}
}
nTried - = nLost ;
nTried - = nLost ;
for ( int b = 0 ; b < nUBuckets ; b + + ) {
std : : set < int > & vNew = vvNew [ b ] ;
// Deserialize positions in the new table (if possible).
for ( int bucket = 0 ; bucket < nUBuckets ; bucket + + ) {
int nSize = 0 ;
int nSize = 0 ;
s > > nSize ;
s > > nSize ;
for ( int n = 0 ; n < nSize ; n + + ) {
for ( int n = 0 ; n < nSize ; n + + ) {
int nIndex = 0 ;
int nIndex = 0 ;
s > > nIndex ;
s > > nIndex ;
CAddrInfo & info = mapInfo [ nIndex ] ;
if ( nIndex > = 0 & & nIndex < nNew ) {
if ( nUBuckets = = ADDRMAN_NEW_BUCKET_COUNT & & info . nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS ) {
CAddrInfo & info = mapInfo [ nIndex ] ;
info . nRefCount + + ;
int nUBucketPos = info . GetBucketPosition ( nKey , true , bucket ) ;
vNew . insert ( nIndex ) ;
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 ;
}
}
}
}
}
}
}
// 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 ( ) ; ) {
if ( it - > second . fInTried = = false & & it - > second . nRefCount = = 0 ) {
std : : map < int , CAddrInfo > : : const_iterator itCopy = it + + ;
Delete ( itCopy - > first ) ;
nLostUnk + + ;
} else {
it + + ;
}
}
if ( nLost + nLostUnk > 0 ) {
LogPrint ( " addrman " , " addrman lost %i new and %i tried addresses due to collisions \n " , nLostUnk , nLost ) ;
}
Check ( ) ;
}
}
unsigned int GetSerializeSize ( int nType , int nVersion ) const
unsigned int GetSerializeSize ( int nType , int nVersion ) const
@ -391,14 +427,34 @@ public:
return ( CSizeComputer ( nType , nVersion ) < < * this ) . size ( ) ;
return ( CSizeComputer ( nType , nVersion ) < < * this ) . size ( ) ;
}
}
CAddrMan ( ) : vRandom ( 0 ) , vvTried ( ADDRMAN_TRIED_BUCKET_COUNT , std : : vector < int > ( 0 ) ) , vvNew ( ADDRMAN_NEW_BUCKET_COUNT , std : : set < int > ( ) )
void Clear ( )
{
{
nKey . resize ( 32 ) ;
std : : vector < int > ( ) . swap ( vRandom ) ;
GetRandBytes ( & nKey [ 0 ] , 32 ) ;
nKey = GetRandHash ( ) ;
for ( size_t bucket = 0 ; bucket < ADDRMAN_NEW_BUCKET_COUNT ; bucket + + ) {
for ( size_t entry = 0 ; entry < ADDRMAN_BUCKET_SIZE ; entry + + ) {
vvNew [ bucket ] [ entry ] = - 1 ;
}
}
for ( size_t bucket = 0 ; bucket < ADDRMAN_TRIED_BUCKET_COUNT ; bucket + + ) {
for ( size_t entry = 0 ; entry < ADDRMAN_BUCKET_SIZE ; entry + + ) {
vvTried [ bucket ] [ entry ] = - 1 ;
}
}
nIdCount = 0 ;
nTried = 0 ;
nNew = 0 ;
}
nIdCount = 0 ;
CAddrMan ( )
nTried = 0 ;
{
nNew = 0 ;
Clear ( ) ;
}
~ CAddrMan ( )
{
nKey . SetNull ( ) ;
}
}
//! Return the number of (unique) addresses in all tables.
//! Return the number of (unique) addresses in all tables.
@ -477,13 +533,13 @@ public:
* Choose an address to connect to .
* Choose an address to connect to .
* nUnkBias determines how much " new " entries are favored over " tried " ones ( 0 - 100 ) .
* nUnkBias determines how much " new " entries are favored over " tried " ones ( 0 - 100 ) .
*/
*/
CAddress Select ( int nUnkBias = 50 )
CAddress Select ( )
{
{
CAddress addrRet ;
CAddress addrRet ;
{
{
LOCK ( cs ) ;
LOCK ( cs ) ;
Check ( ) ;
Check ( ) ;
addrRet = Select_ ( nUnkBias ) ;
addrRet = Select_ ( ) ;
Check ( ) ;
Check ( ) ;
}
}
return addrRet ;
return addrRet ;