From 36e8b4c82ed686d2e57ae39f74e71459c5114a63 Mon Sep 17 00:00:00 2001 From: David Hill <dhill@conformal.com> Date: Thu, 10 Jul 2014 19:03:34 -0500 Subject: [PATCH] organize. no objections from @davecgh --- addrmgr/addrmanager.go | 230 ++++++++++++---------------------------- addrmgr/knownaddress.go | 101 ++++++++++++++++++ 2 files changed, 171 insertions(+), 160 deletions(-) create mode 100644 addrmgr/knownaddress.go diff --git a/addrmgr/addrmanager.go b/addrmgr/addrmanager.go index 1c19b476..16b026bd 100644 --- a/addrmgr/addrmanager.go +++ b/addrmgr/addrmanager.go @@ -26,6 +26,71 @@ import ( "github.com/conformal/btcwire" ) +// AddrManager provides a concurrency safe address manager for caching potential +// peers on the bitcoin network. +type AddrManager struct { + mtx sync.Mutex + dataDir string + lookupFunc func(string) ([]net.IP, error) + rand *rand.Rand + key [32]byte + addrIndex map[string]*knownAddress // address key to ka for all addrs. + addrNew [newBucketCount]map[string]*knownAddress + addrTried [triedBucketCount]*list.List + started int32 + shutdown int32 + wg sync.WaitGroup + quit chan struct{} + nTried int + nNew int + lamtx sync.Mutex + localAddresses map[string]*localAddress +} + +type serializedKnownAddress struct { + Addr string + Src string + Attempts int + TimeStamp int64 + LastAttempt int64 + LastSuccess int64 + // no refcount or tried, that is available from context. +} + +type serializedAddrManager struct { + Version int + Key [32]byte + Addresses []*serializedKnownAddress + NewBuckets [newBucketCount][]string // string is NetAddressKey + TriedBuckets [triedBucketCount][]string +} + +type localAddress struct { + na *btcwire.NetAddress + score AddressPriority +} + +// AddressPriority type is used to describe the heirarchy of local address +// discovery methods. +type AddressPriority int + +const ( + // InterfacePrio signifies the address is on a local interface + InterfacePrio AddressPriority = iota + + // BoundPrio signifies the address has been explicity bounded to. + BoundPrio + + // UpnpPrio signifies the address was obtained from UPnP. + UpnpPrio + + // HTTPPrio signifies the address was obtained from an external HTTP service. + HTTPPrio + + // ManualPrio signifies the address was provided by --externalip. + ManualPrio +) + const ( // needAddressThreshold is the number of addresses under which the // address manager will claim to need more addresses. @@ -94,117 +159,6 @@ const ( serialisationVersion = 1 ) -// knownAddress tracks information about a known network address that is used -// to determine how viable an address is. -type knownAddress struct { - na *btcwire.NetAddress - srcAddr *btcwire.NetAddress - attempts int - lastattempt time.Time - lastsuccess time.Time - tried bool - refs int // reference count of new buckets -} - -// NetAddress returns the underlying btcwire.NetAddress associated with the -// known address. -func (ka *knownAddress) NetAddress() *btcwire.NetAddress { - return ka.na -} - -// LastAttempt returns the last time the known address was attempted. -func (ka *knownAddress) LastAttempt() time.Time { - return ka.lastattempt -} - -// bad returns true if the address in question has not been tried in the last -// minute and meets one of the following criteria: -// 1) It claims to be from the future -// 2) It hasn't been seen in over a month -// 3) It has failed at least three times and never succeeded -// 4) It has failed ten times in the last week -// All addresses that meet these criteria are assumed to be worthless and not -// worth keeping hold of. -func bad(ka *knownAddress) bool { - if ka.lastattempt.After(time.Now().Add(-1 * time.Minute)) { - return false - } - - // From the future? - if ka.na.Timestamp.After(time.Now().Add(10 * time.Minute)) { - return true - } - - // Over a month old? - if ka.na.Timestamp.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { - return true - } - - // Never succeeded? - if ka.lastsuccess.IsZero() && ka.attempts >= numRetries { - return true - } - - // Hasn't succeeded in too long? - if !ka.lastsuccess.After(time.Now().Add(-1*minBadDays*time.Hour*24)) && - ka.attempts >= maxFailures { - return true - } - - return false -} - -// chance returns the selection probability for a known address. The priority -// depends upon how recently the address has been seen, how recently it was last -// attempted and how often attempts to connect to it have failed. -func chance(ka *knownAddress) float64 { - now := time.Now() - lastSeen := now.Sub(ka.na.Timestamp) - lastAttempt := now.Sub(ka.lastattempt) - - if lastSeen < 0 { - lastSeen = 0 - } - if lastAttempt < 0 { - lastAttempt = 0 - } - - c := 600.0 / (600.0 + lastSeen.Seconds()) - - // Very recent attempts are less likely to be retried. - if lastAttempt > 10*time.Minute { - c *= 0.01 - } - - // Failed attempts deprioritise. - for i := ka.attempts; i < 0; i++ { - c /= 1.5 - } - - return c -} - -// AddrManager provides a concurrency safe address manager for caching potential -// peers on the bitcoin network. -type AddrManager struct { - mtx sync.Mutex - dataDir string - lookupFunc func(string) ([]net.IP, error) - rand *rand.Rand - key [32]byte - addrIndex map[string]*knownAddress // address key to ka for all addrs. - addrNew [newBucketCount]map[string]*knownAddress - addrTried [triedBucketCount]*list.List - started int32 - shutdown int32 - wg sync.WaitGroup - quit chan struct{} - nTried int - nNew int - lamtx sync.Mutex - localAddresses map[string]*localAddress -} - // updateAddress is a helper function to either update an address already known // to the address manager, or to add the address if not already known. func (a *AddrManager) updateAddress(netAddr, srcAddr *btcwire.NetAddress) { @@ -273,7 +227,7 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *btcwire.NetAddress) { // Enforce max addresses. if len(a.addrNew[bucket]) > newBucketSize { - log.Tracef("new bucket is full, expiring old ") + log.Tracef("new bucket is full, expiring old") a.expireNew(bucket) } @@ -295,7 +249,7 @@ func (a *AddrManager) expireNew(bucket int) { // use that information instead. var oldest *knownAddress for k, v := range a.addrNew[bucket] { - if bad(v) { + if v.isBad() { log.Tracef("expiring bad address %v", k) delete(a.addrNew[bucket], k) v.refs-- @@ -303,7 +257,7 @@ func (a *AddrManager) expireNew(bucket int) { a.nNew-- delete(a.addrIndex, k) } - return + continue } if oldest == nil { oldest = v @@ -404,24 +358,6 @@ out: log.Trace("Address handler done") } -type serializedKnownAddress struct { - Addr string - Src string - Attempts int - TimeStamp int64 - LastAttempt int64 - LastSuccess int64 - // no refcount or tried, that is available from context. -} - -type serializedAddrManager struct { - Version int - Key [32]byte - Addresses []*serializedKnownAddress - NewBuckets [newBucketCount][]string // string is NetAddressKey - TriedBuckets [triedBucketCount][]string -} - // savePeers saves all the known addresses to a file so they can be read back // in at next run. func (a *AddrManager) savePeers() { @@ -846,7 +782,7 @@ func (a *AddrManager) GetAddress(class string, newBias int) *knownAddress { } ka := e.Value.(*knownAddress) randval := a.rand.Intn(large) - if float64(randval) < (factor * chance(ka) * float64(large)) { + if float64(randval) < (factor * ka.chance() * float64(large)) { log.Tracef("Selected %v from tried bucket", NetAddressKey(ka.na)) return ka @@ -874,7 +810,7 @@ func (a *AddrManager) GetAddress(class string, newBias int) *knownAddress { nth-- } randval := a.rand.Intn(large) - if float64(randval) < (factor * chance(ka) * float64(large)) { + if float64(randval) < (factor * ka.chance() * float64(large)) { log.Tracef("Selected %v from new bucket", NetAddressKey(ka.na)) return ka @@ -1017,32 +953,6 @@ func (a *AddrManager) Good(addr *btcwire.NetAddress) { a.addrNew[newBucket][rmkey] = rmka } -// AddressPriority type is used to describe the heirarchy of local address -// discovery methods. -type AddressPriority int - -const ( - // InterfacePrio signifies the address is on a local interface - InterfacePrio AddressPriority = iota - - // BoundPrio signifies the address has been explicity bounded to. - BoundPrio - - // UpnpPrio signifies the address was obtained from UPnP. - UpnpPrio - - // HTTPPrio signifies the address was obtained from an external HTTP service. - HTTPPrio - - // ManualPrio signifies the address was provided by --externalip. - ManualPrio -) - -type localAddress struct { - na *btcwire.NetAddress - score AddressPriority -} - // AddLocalAddress adds na to the list of known local addresses to advertise // with the given priority. func (a *AddrManager) AddLocalAddress(na *btcwire.NetAddress, priority AddressPriority) error { diff --git a/addrmgr/knownaddress.go b/addrmgr/knownaddress.go new file mode 100644 index 00000000..f027fc17 --- /dev/null +++ b/addrmgr/knownaddress.go @@ -0,0 +1,101 @@ +// Copyright (c) 2013-2014 Conformal Systems LLC. +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package addrmgr + +import ( + "time" + + "github.com/conformal/btcwire" +) + +// knownAddress tracks information about a known network address that is used +// to determine how viable an address is. +type knownAddress struct { + na *btcwire.NetAddress + srcAddr *btcwire.NetAddress + attempts int + lastattempt time.Time + lastsuccess time.Time + tried bool + refs int // reference count of new buckets +} + +// NetAddress returns the underlying btcwire.NetAddress associated with the +// known address. +func (ka *knownAddress) NetAddress() *btcwire.NetAddress { + return ka.na +} + +// LastAttempt returns the last time the known address was attempted. +func (ka *knownAddress) LastAttempt() time.Time { + return ka.lastattempt +} + +// chance returns the selection probability for a known address. The priority +// depends upon how recently the address has been seen, how recently it was last +// attempted and how often attempts to connect to it have failed. +func (ka *knownAddress) chance() float64 { + now := time.Now() + lastSeen := now.Sub(ka.na.Timestamp) + lastAttempt := now.Sub(ka.lastattempt) + + if lastSeen < 0 { + lastSeen = 0 + } + if lastAttempt < 0 { + lastAttempt = 0 + } + + c := 600.0 / (600.0 + lastSeen.Seconds()) + + // Very recent attempts are less likely to be retried. + if lastAttempt > 10*time.Minute { + c *= 0.01 + } + + // Failed attempts deprioritise. + for i := ka.attempts; i < 0; i++ { + c /= 1.5 + } + + return c +} + +// isBad returns true if the address in question has not been tried in the last +// minute and meets one of the following criteria: +// 1) It claims to be from the future +// 2) It hasn't been seen in over a month +// 3) It has failed at least three times and never succeeded +// 4) It has failed ten times in the last week +// All addresses that meet these criteria are assumed to be worthless and not +// worth keeping hold of. +func (ka *knownAddress) isBad() bool { + if ka.lastattempt.After(time.Now().Add(-1 * time.Minute)) { + return false + } + + // From the future? + if ka.na.Timestamp.After(time.Now().Add(10 * time.Minute)) { + return true + } + + // Over a month old? + if ka.na.Timestamp.After(time.Now().Add(-1 * numMissingDays * time.Hour * 24)) { + return true + } + + // Never succeeded? + if ka.lastsuccess.IsZero() && ka.attempts >= numRetries { + return true + } + + // Hasn't succeeded in too long? + if !ka.lastsuccess.After(time.Now().Add(-1*minBadDays*time.Hour*24)) && + ka.attempts >= maxFailures { + return true + } + + return false +}