|
@ -7,7 +7,6 @@ package pex |
|
|
import ( |
|
|
import ( |
|
|
"crypto/sha256" |
|
|
"crypto/sha256" |
|
|
"encoding/binary" |
|
|
"encoding/binary" |
|
|
"fmt" |
|
|
|
|
|
"math" |
|
|
"math" |
|
|
"net" |
|
|
"net" |
|
|
"sync" |
|
|
"sync" |
|
@ -169,7 +168,9 @@ func (a *addrBook) OurAddress(addr *p2p.NetAddress) bool { |
|
|
return ok |
|
|
return ok |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// AddAddress implements AddrBook - adds the given address as received from the given source.
|
|
|
|
|
|
|
|
|
// AddAddress implements AddrBook
|
|
|
|
|
|
// Add address to a "new" bucket. If it's already in one, only add it probabilistically.
|
|
|
|
|
|
// Returns error if the addr is non-routable. Does not add self.
|
|
|
// NOTE: addr must not be nil
|
|
|
// NOTE: addr must not be nil
|
|
|
func (a *addrBook) AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error { |
|
|
func (a *addrBook) AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error { |
|
|
a.mtx.Lock() |
|
|
a.mtx.Lock() |
|
@ -220,7 +221,11 @@ func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress { |
|
|
a.mtx.Lock() |
|
|
a.mtx.Lock() |
|
|
defer a.mtx.Unlock() |
|
|
defer a.mtx.Unlock() |
|
|
|
|
|
|
|
|
if a.size() == 0 { |
|
|
|
|
|
|
|
|
bookSize := a.size() |
|
|
|
|
|
if bookSize <= 0 { |
|
|
|
|
|
if bookSize < 0 { |
|
|
|
|
|
a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) |
|
|
|
|
|
} |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
if biasTowardsNewAddrs > 100 { |
|
|
if biasTowardsNewAddrs > 100 { |
|
@ -294,29 +299,35 @@ func (a *addrBook) MarkBad(addr *p2p.NetAddress) { |
|
|
|
|
|
|
|
|
// GetSelection implements AddrBook.
|
|
|
// GetSelection implements AddrBook.
|
|
|
// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
|
|
|
// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
|
|
|
|
|
|
// Must never return a nil address.
|
|
|
func (a *addrBook) GetSelection() []*p2p.NetAddress { |
|
|
func (a *addrBook) GetSelection() []*p2p.NetAddress { |
|
|
a.mtx.Lock() |
|
|
a.mtx.Lock() |
|
|
defer a.mtx.Unlock() |
|
|
defer a.mtx.Unlock() |
|
|
|
|
|
|
|
|
if a.size() == 0 { |
|
|
|
|
|
|
|
|
bookSize := a.size() |
|
|
|
|
|
if bookSize <= 0 { |
|
|
|
|
|
if bookSize < 0 { |
|
|
|
|
|
a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) |
|
|
|
|
|
} |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
allAddr := make([]*p2p.NetAddress, a.size()) |
|
|
|
|
|
|
|
|
numAddresses := cmn.MaxInt( |
|
|
|
|
|
cmn.MinInt(minGetSelection, bookSize), |
|
|
|
|
|
bookSize*getSelectionPercent/100) |
|
|
|
|
|
numAddresses = cmn.MinInt(maxGetSelection, numAddresses) |
|
|
|
|
|
|
|
|
|
|
|
// XXX: instead of making a list of all addresses, shuffling, and slicing a random chunk,
|
|
|
|
|
|
// could we just select a random numAddresses of indexes?
|
|
|
|
|
|
allAddr := make([]*p2p.NetAddress, bookSize) |
|
|
i := 0 |
|
|
i := 0 |
|
|
for _, ka := range a.addrLookup { |
|
|
for _, ka := range a.addrLookup { |
|
|
allAddr[i] = ka.Addr |
|
|
allAddr[i] = ka.Addr |
|
|
i++ |
|
|
i++ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
numAddresses := cmn.MaxInt( |
|
|
|
|
|
cmn.MinInt(minGetSelection, len(allAddr)), |
|
|
|
|
|
len(allAddr)*getSelectionPercent/100) |
|
|
|
|
|
numAddresses = cmn.MinInt(maxGetSelection, numAddresses) |
|
|
|
|
|
|
|
|
|
|
|
// Fisher-Yates shuffle the array. We only need to do the first
|
|
|
// Fisher-Yates shuffle the array. We only need to do the first
|
|
|
// `numAddresses' since we are throwing the rest.
|
|
|
// `numAddresses' since we are throwing the rest.
|
|
|
// XXX: What's the point of this if we already loop randomly through addrLookup ?
|
|
|
|
|
|
for i := 0; i < numAddresses; i++ { |
|
|
for i := 0; i < numAddresses; i++ { |
|
|
// pick a number between current index and the end
|
|
|
// pick a number between current index and the end
|
|
|
j := cmn.RandIntn(len(allAddr)-i) + i |
|
|
j := cmn.RandIntn(len(allAddr)-i) + i |
|
@ -329,6 +340,7 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress { |
|
|
|
|
|
|
|
|
// GetSelectionWithBias implements AddrBook.
|
|
|
// GetSelectionWithBias implements AddrBook.
|
|
|
// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
|
|
|
// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
|
|
|
|
|
|
// Must never return a nil address.
|
|
|
//
|
|
|
//
|
|
|
// Each address is picked randomly from an old or new bucket according to the
|
|
|
// Each address is picked randomly from an old or new bucket according to the
|
|
|
// biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to
|
|
|
// biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to
|
|
@ -338,7 +350,11 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre |
|
|
a.mtx.Lock() |
|
|
a.mtx.Lock() |
|
|
defer a.mtx.Unlock() |
|
|
defer a.mtx.Unlock() |
|
|
|
|
|
|
|
|
if a.size() == 0 { |
|
|
|
|
|
|
|
|
bookSize := a.size() |
|
|
|
|
|
if bookSize <= 0 { |
|
|
|
|
|
if bookSize < 0 { |
|
|
|
|
|
a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) |
|
|
|
|
|
} |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -350,8 +366,8 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
numAddresses := cmn.MaxInt( |
|
|
numAddresses := cmn.MaxInt( |
|
|
cmn.MinInt(minGetSelection, a.size()), |
|
|
|
|
|
a.size()*getSelectionPercent/100) |
|
|
|
|
|
|
|
|
cmn.MinInt(minGetSelection, bookSize), |
|
|
|
|
|
bookSize*getSelectionPercent/100) |
|
|
numAddresses = cmn.MinInt(maxGetSelection, numAddresses) |
|
|
numAddresses = cmn.MinInt(maxGetSelection, numAddresses) |
|
|
|
|
|
|
|
|
selection := make([]*p2p.NetAddress, numAddresses) |
|
|
selection := make([]*p2p.NetAddress, numAddresses) |
|
@ -487,11 +503,11 @@ func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAd |
|
|
|
|
|
|
|
|
// Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full.
|
|
|
// Adds ka to new bucket. Returns false if it couldn't do it cuz buckets full.
|
|
|
// NOTE: currently it always returns true.
|
|
|
// NOTE: currently it always returns true.
|
|
|
func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { |
|
|
|
|
|
|
|
|
func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) { |
|
|
// Sanity check
|
|
|
// Sanity check
|
|
|
if ka.isOld() { |
|
|
if ka.isOld() { |
|
|
a.Logger.Error(cmn.Fmt("Cannot add address already in old bucket to a new bucket: %v", ka)) |
|
|
|
|
|
return false |
|
|
|
|
|
|
|
|
a.Logger.Error("Failed Sanity Check! Cant add old address to new bucket", "ka", ka, "bucket", bucketIdx) |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
addrStr := ka.Addr.String() |
|
|
addrStr := ka.Addr.String() |
|
@ -499,7 +515,7 @@ func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { |
|
|
|
|
|
|
|
|
// Already exists?
|
|
|
// Already exists?
|
|
|
if _, ok := bucket[addrStr]; ok { |
|
|
if _, ok := bucket[addrStr]; ok { |
|
|
return true |
|
|
|
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Enforce max addresses.
|
|
|
// Enforce max addresses.
|
|
@ -517,8 +533,6 @@ func (a *addrBook) addToNewBucket(ka *knownAddress, bucketIdx int) bool { |
|
|
|
|
|
|
|
|
// Add it to addrLookup
|
|
|
// Add it to addrLookup
|
|
|
a.addrLookup[ka.ID()] = ka |
|
|
a.addrLookup[ka.ID()] = ka |
|
|
|
|
|
|
|
|
return true |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full.
|
|
|
// Adds ka to old bucket. Returns false if it couldn't do it cuz buckets full.
|
|
@ -605,19 +619,22 @@ func (a *addrBook) pickOldest(bucketType byte, bucketIdx int) *knownAddress { |
|
|
// adds the address to a "new" bucket. if its already in one,
|
|
|
// adds the address to a "new" bucket. if its already in one,
|
|
|
// it only adds it probabilistically
|
|
|
// it only adds it probabilistically
|
|
|
func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { |
|
|
func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { |
|
|
|
|
|
if addr == nil || src == nil { |
|
|
|
|
|
return ErrAddrBookNilAddr{addr, src} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if a.routabilityStrict && !addr.Routable() { |
|
|
if a.routabilityStrict && !addr.Routable() { |
|
|
return fmt.Errorf("Cannot add non-routable address %v", addr) |
|
|
|
|
|
|
|
|
return ErrAddrBookNonRoutable{addr} |
|
|
} |
|
|
} |
|
|
|
|
|
// TODO: we should track ourAddrs by ID and by IP:PORT and refuse both.
|
|
|
if _, ok := a.ourAddrs[addr.String()]; ok { |
|
|
if _, ok := a.ourAddrs[addr.String()]; ok { |
|
|
// Ignore our own listener address.
|
|
|
|
|
|
return fmt.Errorf("Cannot add ourselves with address %v", addr) |
|
|
|
|
|
|
|
|
return ErrAddrBookSelf{addr} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ka := a.addrLookup[addr.ID] |
|
|
ka := a.addrLookup[addr.ID] |
|
|
|
|
|
|
|
|
if ka != nil { |
|
|
if ka != nil { |
|
|
// Already old.
|
|
|
|
|
|
if ka.isOld() { |
|
|
|
|
|
|
|
|
// If its already old and the addr is the same, ignore it.
|
|
|
|
|
|
if ka.isOld() && ka.Addr.Equals(addr) { |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
// Already in max new buckets.
|
|
|
// Already in max new buckets.
|
|
@ -634,12 +651,7 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bucket := a.calcNewBucket(addr, src) |
|
|
bucket := a.calcNewBucket(addr, src) |
|
|
added := a.addToNewBucket(ka, bucket) |
|
|
|
|
|
if !added { |
|
|
|
|
|
a.Logger.Info("Can't add new address, addr book is full", "address", addr, "total", a.size()) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
a.Logger.Info("Added new address", "address", addr, "total", a.size()) |
|
|
|
|
|
|
|
|
a.addToNewBucket(ka, bucket) |
|
|
return nil |
|
|
return nil |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -674,8 +686,6 @@ func (a *addrBook) moveToOld(ka *knownAddress) { |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Remember one of the buckets in which ka is in.
|
|
|
|
|
|
freedBucket := ka.Buckets[0] |
|
|
|
|
|
// Remove from all (new) buckets.
|
|
|
// Remove from all (new) buckets.
|
|
|
a.removeFromAllBuckets(ka) |
|
|
a.removeFromAllBuckets(ka) |
|
|
// It's officially old now.
|
|
|
// It's officially old now.
|
|
@ -685,20 +695,13 @@ func (a *addrBook) moveToOld(ka *knownAddress) { |
|
|
oldBucketIdx := a.calcOldBucket(ka.Addr) |
|
|
oldBucketIdx := a.calcOldBucket(ka.Addr) |
|
|
added := a.addToOldBucket(ka, oldBucketIdx) |
|
|
added := a.addToOldBucket(ka, oldBucketIdx) |
|
|
if !added { |
|
|
if !added { |
|
|
// No room, must evict something
|
|
|
|
|
|
|
|
|
// No room; move the oldest to a new bucket
|
|
|
oldest := a.pickOldest(bucketTypeOld, oldBucketIdx) |
|
|
oldest := a.pickOldest(bucketTypeOld, oldBucketIdx) |
|
|
a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx) |
|
|
a.removeFromBucket(oldest, bucketTypeOld, oldBucketIdx) |
|
|
// Find new bucket to put oldest in
|
|
|
|
|
|
newBucketIdx := a.calcNewBucket(oldest.Addr, oldest.Src) |
|
|
newBucketIdx := a.calcNewBucket(oldest.Addr, oldest.Src) |
|
|
added := a.addToNewBucket(oldest, newBucketIdx) |
|
|
|
|
|
// No space in newBucket either, just put it in freedBucket from above.
|
|
|
|
|
|
if !added { |
|
|
|
|
|
added := a.addToNewBucket(oldest, freedBucket) |
|
|
|
|
|
if !added { |
|
|
|
|
|
a.Logger.Error(cmn.Fmt("Could not migrate oldest %v to freedBucket %v", oldest, freedBucket)) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
// Finally, add to bucket again.
|
|
|
|
|
|
|
|
|
a.addToNewBucket(oldest, newBucketIdx) |
|
|
|
|
|
|
|
|
|
|
|
// Finally, add our ka to old bucket again.
|
|
|
added = a.addToOldBucket(ka, oldBucketIdx) |
|
|
added = a.addToOldBucket(ka, oldBucketIdx) |
|
|
if !added { |
|
|
if !added { |
|
|
a.Logger.Error(cmn.Fmt("Could not re-add ka %v to oldBucketIdx %v", ka, oldBucketIdx)) |
|
|
a.Logger.Error(cmn.Fmt("Could not re-add ka %v to oldBucketIdx %v", ka, oldBucketIdx)) |
|
|