mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[DEV-336] Update peer package to accommodate partial nodes (#176)
* [DEV-336] Split addresses in address manager by subnetwork id * [DEV-336] Load DAG with subnetwork from the config file * [DEV-336] Remove redundant checks in updateAddrNew and updateAddrTried
This commit is contained in:
parent
a82e6ae24a
commit
1e09d470f7
@ -22,47 +22,57 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/daglabs/btcd/util/subnetworkid"
|
||||
|
||||
"github.com/daglabs/btcd/dagconfig/daghash"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
|
||||
type newBucket [newBucketCount]map[string]*KnownAddress
|
||||
type triedBucket [triedBucketCount]*list.List
|
||||
|
||||
// AddrManager provides a concurrency safe address manager for caching potential
|
||||
// peers on the bitcoin network.
|
||||
type AddrManager struct {
|
||||
mtx sync.Mutex
|
||||
peersFile 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
|
||||
mtx sync.Mutex
|
||||
peersFile 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 map[subnetworkid.SubnetworkID]*newBucket
|
||||
addrTried map[subnetworkid.SubnetworkID]*triedBucket
|
||||
started int32
|
||||
shutdown int32
|
||||
wg sync.WaitGroup
|
||||
quit chan struct{}
|
||||
nTried map[subnetworkid.SubnetworkID]int
|
||||
nNew map[subnetworkid.SubnetworkID]int
|
||||
lamtx sync.Mutex
|
||||
localAddresses map[string]*localAddress
|
||||
localSubnetworkID *subnetworkid.SubnetworkID
|
||||
}
|
||||
|
||||
type serializedKnownAddress struct {
|
||||
Addr string
|
||||
Src string
|
||||
Attempts int
|
||||
TimeStamp int64
|
||||
LastAttempt int64
|
||||
LastSuccess int64
|
||||
Addr string
|
||||
Src string
|
||||
SubnetworkID string
|
||||
Attempts int
|
||||
TimeStamp int64
|
||||
LastAttempt int64
|
||||
LastSuccess int64
|
||||
// no refcount or tried, that is available from context.
|
||||
}
|
||||
|
||||
type serializedNewBucket [newBucketCount][]string
|
||||
type serializedTriedBucket [triedBucketCount][]string
|
||||
|
||||
type serializedAddrManager struct {
|
||||
Version int
|
||||
Key [32]byte
|
||||
Addresses []*serializedKnownAddress
|
||||
NewBuckets [newBucketCount][]string // string is NetAddressKey
|
||||
TriedBuckets [triedBucketCount][]string
|
||||
NewBuckets map[string]*serializedNewBucket // string is NetAddressKey
|
||||
TriedBuckets map[string]*serializedTriedBucket
|
||||
}
|
||||
|
||||
type localAddress struct {
|
||||
@ -206,80 +216,105 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *wire.NetAddress) {
|
||||
// updated elsewhere in the addrmanager code and would otherwise
|
||||
// change the actual netaddress on the peer.
|
||||
netAddrCopy := *netAddr
|
||||
ka = &KnownAddress{na: &netAddrCopy, srcAddr: srcAddr}
|
||||
ka = &KnownAddress{na: &netAddrCopy, srcAddr: srcAddr, subnetworkID: &wire.SubnetworkIDUnknown}
|
||||
a.addrIndex[addr] = ka
|
||||
a.nNew++
|
||||
a.nNew[wire.SubnetworkIDUnknown]++
|
||||
// XXX time penalty?
|
||||
}
|
||||
|
||||
bucket := a.getNewBucket(netAddr, srcAddr)
|
||||
|
||||
// Already exists?
|
||||
if _, ok := a.addrNew[bucket][addr]; ok {
|
||||
return
|
||||
if a.addrNew[*ka.subnetworkID] != nil {
|
||||
if _, ok := a.addrNew[*ka.subnetworkID][bucket][addr]; ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Enforce max addresses.
|
||||
if len(a.addrNew[bucket]) > newBucketSize {
|
||||
if a.addrNew[*ka.subnetworkID] != nil && len(a.addrNew[*ka.subnetworkID][bucket]) > newBucketSize {
|
||||
log.Tracef("new bucket is full, expiring old")
|
||||
a.expireNew(bucket)
|
||||
}
|
||||
|
||||
// Add to new bucket.
|
||||
ka.refs++
|
||||
a.addrNew[bucket][addr] = ka
|
||||
a.updateAddrNew(bucket, addr, ka)
|
||||
|
||||
log.Tracef("Added new address %s for a total of %d addresses", addr,
|
||||
a.nTried+a.nNew)
|
||||
a.nTried[*ka.subnetworkID]+a.nNew[*ka.subnetworkID])
|
||||
}
|
||||
|
||||
func (a *AddrManager) updateAddrNew(bucket int, addr string, ka *KnownAddress) {
|
||||
if _, ok := a.addrNew[*ka.subnetworkID]; !ok {
|
||||
a.addrNew[*ka.subnetworkID] = &newBucket{}
|
||||
for i := range a.addrNew[*ka.subnetworkID] {
|
||||
a.addrNew[*ka.subnetworkID][i] = make(map[string]*KnownAddress)
|
||||
}
|
||||
}
|
||||
a.addrNew[*ka.subnetworkID][bucket][addr] = ka
|
||||
}
|
||||
|
||||
func (a *AddrManager) updateAddrTried(bucket int, ka *KnownAddress) {
|
||||
if _, ok := a.addrTried[*ka.subnetworkID]; !ok {
|
||||
a.addrTried[*ka.subnetworkID] = &triedBucket{}
|
||||
for i := range a.addrTried[*ka.subnetworkID] {
|
||||
a.addrTried[*ka.subnetworkID][i] = list.New()
|
||||
}
|
||||
}
|
||||
a.addrTried[*ka.subnetworkID][bucket].PushBack(ka)
|
||||
}
|
||||
|
||||
// expireNew makes space in the new buckets by expiring the really bad entries.
|
||||
// If no bad entries are available we look at a few and remove the oldest.
|
||||
func (a *AddrManager) expireNew(bucket int) {
|
||||
// First see if there are any entries that are so bad we can just throw
|
||||
// them away. otherwise we throw away the oldest entry in the cache.
|
||||
// Bitcoind here chooses four random and just throws the oldest of
|
||||
// those away, but we keep track of oldest in the initial traversal and
|
||||
// use that information instead.
|
||||
var oldest *KnownAddress
|
||||
for k, v := range a.addrNew[bucket] {
|
||||
if v.isBad() {
|
||||
log.Tracef("expiring bad address %v", k)
|
||||
delete(a.addrNew[bucket], k)
|
||||
v.refs--
|
||||
if v.refs == 0 {
|
||||
a.nNew--
|
||||
delete(a.addrIndex, k)
|
||||
for subnetworkID := range a.addrNew {
|
||||
// First see if there are any entries that are so bad we can just throw
|
||||
// them away. otherwise we throw away the oldest entry in the cache.
|
||||
// Bitcoind here chooses four random and just throws the oldest of
|
||||
// those away, but we keep track of oldest in the initial traversal and
|
||||
// use that information instead.
|
||||
var oldest *KnownAddress
|
||||
for k, v := range a.addrNew[subnetworkID][bucket] {
|
||||
if v.isBad() {
|
||||
log.Tracef("expiring bad address %v", k)
|
||||
delete(a.addrNew[subnetworkID][bucket], k)
|
||||
v.refs--
|
||||
if v.refs == 0 {
|
||||
a.nNew[subnetworkID]--
|
||||
delete(a.addrIndex, k)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if oldest == nil {
|
||||
oldest = v
|
||||
} else if !v.na.Timestamp.After(oldest.na.Timestamp) {
|
||||
oldest = v
|
||||
}
|
||||
continue
|
||||
}
|
||||
if oldest == nil {
|
||||
oldest = v
|
||||
} else if !v.na.Timestamp.After(oldest.na.Timestamp) {
|
||||
oldest = v
|
||||
|
||||
if oldest != nil {
|
||||
key := NetAddressKey(oldest.na)
|
||||
log.Tracef("expiring oldest address %v", key)
|
||||
|
||||
delete(a.addrNew[subnetworkID][bucket], key)
|
||||
oldest.refs--
|
||||
if oldest.refs == 0 {
|
||||
a.nNew[subnetworkID]--
|
||||
delete(a.addrIndex, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if oldest != nil {
|
||||
key := NetAddressKey(oldest.na)
|
||||
log.Tracef("expiring oldest address %v", key)
|
||||
|
||||
delete(a.addrNew[bucket], key)
|
||||
oldest.refs--
|
||||
if oldest.refs == 0 {
|
||||
a.nNew--
|
||||
delete(a.addrIndex, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pickTried selects an address from the tried bucket to be evicted.
|
||||
// We just choose the eldest. Bitcoind selects 4 random entries and throws away
|
||||
// the older of them.
|
||||
func (a *AddrManager) pickTried(bucket int) *list.Element {
|
||||
func (a *AddrManager) pickTried(subnetworkID *subnetworkid.SubnetworkID, bucket int) *list.Element {
|
||||
var oldest *KnownAddress
|
||||
var oldestElem *list.Element
|
||||
for e := a.addrTried[bucket].Front(); e != nil; e = e.Next() {
|
||||
for e := a.addrTried[*subnetworkID][bucket].Front(); e != nil; e = e.Next() {
|
||||
ka := e.Value.(*KnownAddress)
|
||||
if oldest == nil || oldest.na.Timestamp.After(ka.na.Timestamp) {
|
||||
oldestElem = e
|
||||
@ -369,6 +404,7 @@ func (a *AddrManager) savePeers() {
|
||||
for k, v := range a.addrIndex {
|
||||
ska := new(serializedKnownAddress)
|
||||
ska.Addr = k
|
||||
ska.SubnetworkID = v.subnetworkID.String()
|
||||
ska.TimeStamp = v.na.Timestamp.Unix()
|
||||
ska.Src = NetAddressKey(v.srcAddr)
|
||||
ska.Attempts = v.attempts
|
||||
@ -379,21 +415,35 @@ func (a *AddrManager) savePeers() {
|
||||
sam.Addresses[i] = ska
|
||||
i++
|
||||
}
|
||||
for i := range a.addrNew {
|
||||
sam.NewBuckets[i] = make([]string, len(a.addrNew[i]))
|
||||
j := 0
|
||||
for k := range a.addrNew[i] {
|
||||
sam.NewBuckets[i][j] = k
|
||||
j++
|
||||
|
||||
sam.NewBuckets = make(map[string]*serializedNewBucket)
|
||||
for subnetworkID := range a.addrNew {
|
||||
subnetworkIDStr := subnetworkID.String()
|
||||
sam.NewBuckets[subnetworkIDStr] = &serializedNewBucket{}
|
||||
|
||||
for i := range a.addrNew[subnetworkID] {
|
||||
sam.NewBuckets[subnetworkIDStr][i] = make([]string, len(a.addrNew[subnetworkID][i]))
|
||||
j := 0
|
||||
for k := range a.addrNew[subnetworkID][i] {
|
||||
sam.NewBuckets[subnetworkIDStr][i][j] = k
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range a.addrTried {
|
||||
sam.TriedBuckets[i] = make([]string, a.addrTried[i].Len())
|
||||
j := 0
|
||||
for e := a.addrTried[i].Front(); e != nil; e = e.Next() {
|
||||
ka := e.Value.(*KnownAddress)
|
||||
sam.TriedBuckets[i][j] = NetAddressKey(ka.na)
|
||||
j++
|
||||
|
||||
sam.TriedBuckets = make(map[string]*serializedTriedBucket)
|
||||
for subnetworkID := range a.addrTried {
|
||||
subnetworkIDStr := subnetworkID.String()
|
||||
sam.TriedBuckets[subnetworkIDStr] = &serializedTriedBucket{}
|
||||
|
||||
for i := range a.addrTried[subnetworkID] {
|
||||
sam.TriedBuckets[subnetworkIDStr][i] = make([]string, a.addrTried[subnetworkID][i].Len())
|
||||
j := 0
|
||||
for e := a.addrTried[subnetworkID][i].Front(); e != nil; e = e.Next() {
|
||||
ka := e.Value.(*KnownAddress)
|
||||
sam.TriedBuckets[subnetworkIDStr][i][j] = NetAddressKey(ka.na)
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,7 +478,7 @@ func (a *AddrManager) loadPeers() {
|
||||
a.reset()
|
||||
return
|
||||
}
|
||||
log.Infof("Loaded %d addresses from file '%s'", a.numAddresses(), a.peersFile)
|
||||
log.Infof("Loaded %d addresses from file '%s'", a.totalNumAddresses(), a.peersFile)
|
||||
}
|
||||
|
||||
func (a *AddrManager) deserializePeers(filePath string) error {
|
||||
@ -468,38 +518,55 @@ func (a *AddrManager) deserializePeers(filePath string) error {
|
||||
return fmt.Errorf("failed to deserialize netaddress "+
|
||||
"%s: %v", v.Src, err)
|
||||
}
|
||||
ka.subnetworkID, err = subnetworkid.NewFromStr(v.SubnetworkID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialize subnetwork id "+
|
||||
"%s: %v", v.SubnetworkID, err)
|
||||
}
|
||||
ka.attempts = v.Attempts
|
||||
ka.lastattempt = time.Unix(v.LastAttempt, 0)
|
||||
ka.lastsuccess = time.Unix(v.LastSuccess, 0)
|
||||
a.addrIndex[NetAddressKey(ka.na)] = ka
|
||||
}
|
||||
|
||||
for i := range sam.NewBuckets {
|
||||
for _, val := range sam.NewBuckets[i] {
|
||||
ka, ok := a.addrIndex[val]
|
||||
if !ok {
|
||||
return fmt.Errorf("newbucket contains %s but "+
|
||||
"none in address list", val)
|
||||
}
|
||||
for subnetworkIDStr := range sam.NewBuckets {
|
||||
subnetworkID, err := subnetworkid.NewFromStr(subnetworkIDStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range sam.NewBuckets[subnetworkIDStr] {
|
||||
for _, val := range sam.NewBuckets[subnetworkIDStr][i] {
|
||||
ka, ok := a.addrIndex[val]
|
||||
if !ok {
|
||||
return fmt.Errorf("newbucket contains %s but "+
|
||||
"none in address list", val)
|
||||
}
|
||||
|
||||
if ka.refs == 0 {
|
||||
a.nNew++
|
||||
if ka.refs == 0 {
|
||||
a.nNew[*subnetworkID]++
|
||||
}
|
||||
ka.refs++
|
||||
a.addrNew[*subnetworkID][i][val] = ka
|
||||
}
|
||||
ka.refs++
|
||||
a.addrNew[i][val] = ka
|
||||
}
|
||||
}
|
||||
for i := range sam.TriedBuckets {
|
||||
for _, val := range sam.TriedBuckets[i] {
|
||||
ka, ok := a.addrIndex[val]
|
||||
if !ok {
|
||||
return fmt.Errorf("Newbucket contains %s but "+
|
||||
"none in address list", val)
|
||||
}
|
||||
for subnetworkIDStr := range sam.NewBuckets {
|
||||
subnetworkID, err := subnetworkid.NewFromStr(subnetworkIDStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range sam.TriedBuckets[subnetworkIDStr] {
|
||||
for _, val := range sam.TriedBuckets[subnetworkIDStr][i] {
|
||||
ka, ok := a.addrIndex[val]
|
||||
if !ok {
|
||||
return fmt.Errorf("Newbucket contains %s but "+
|
||||
"none in address list", val)
|
||||
}
|
||||
|
||||
ka.tried = true
|
||||
a.nTried++
|
||||
a.addrTried[i].PushBack(ka)
|
||||
ka.tried = true
|
||||
a.nTried[*subnetworkID]++
|
||||
a.addrTried[*subnetworkID][i].PushBack(ka)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,17 +676,30 @@ func (a *AddrManager) AddAddressByIP(addrIP string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NumAddresses returns the number of addresses known to the address manager.
|
||||
func (a *AddrManager) numAddresses() int {
|
||||
return a.nTried + a.nNew
|
||||
// numAddresses returns the number of addresses that belongs to a specific subnetwork id
|
||||
// which are known to the address manager.
|
||||
func (a *AddrManager) numAddresses(subnetworkID *subnetworkid.SubnetworkID) int {
|
||||
return a.nTried[*subnetworkID] + a.nNew[*subnetworkID]
|
||||
}
|
||||
|
||||
// NumAddresses returns the number of addresses known to the address manager.
|
||||
func (a *AddrManager) NumAddresses() int {
|
||||
// totalNumAddresses returns the number of addresses known to the address manager.
|
||||
func (a *AddrManager) totalNumAddresses() int {
|
||||
total := 0
|
||||
for _, numAddresses := range a.nTried {
|
||||
total += numAddresses
|
||||
}
|
||||
for _, numAddresses := range a.nNew {
|
||||
total += numAddresses
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// TotalNumAddresses returns the number of addresses known to the address manager.
|
||||
func (a *AddrManager) TotalNumAddresses() int {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
return a.numAddresses()
|
||||
return a.totalNumAddresses()
|
||||
}
|
||||
|
||||
// NeedMoreAddresses returns whether or not the address manager needs more
|
||||
@ -628,7 +708,7 @@ func (a *AddrManager) NeedMoreAddresses() bool {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
return a.numAddresses() < needAddressThreshold
|
||||
return a.numAddresses(a.localSubnetworkID)+a.numAddresses(&wire.SubnetworkIDUnknown) < needAddressThreshold
|
||||
}
|
||||
|
||||
// AddressCache returns the current address cache. It must be treated as
|
||||
@ -673,12 +753,11 @@ func (a *AddrManager) reset() {
|
||||
|
||||
// fill key with bytes from a good random source.
|
||||
io.ReadFull(crand.Reader, a.key[:])
|
||||
for i := range a.addrNew {
|
||||
a.addrNew[i] = make(map[string]*KnownAddress)
|
||||
}
|
||||
for i := range a.addrTried {
|
||||
a.addrTried[i] = list.New()
|
||||
}
|
||||
a.addrNew = make(map[subnetworkid.SubnetworkID]*newBucket)
|
||||
a.addrTried = make(map[subnetworkid.SubnetworkID]*triedBucket)
|
||||
|
||||
a.nNew = make(map[subnetworkid.SubnetworkID]int)
|
||||
a.nTried = make(map[subnetworkid.SubnetworkID]int)
|
||||
}
|
||||
|
||||
// HostToNetAddress returns a netaddress given a host address. If the address
|
||||
@ -742,26 +821,27 @@ func (a *AddrManager) GetAddress() *KnownAddress {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
if a.numAddresses() == 0 {
|
||||
return nil
|
||||
subnetworkID := *a.localSubnetworkID
|
||||
if a.nTried[subnetworkID] == 0 && a.nNew[subnetworkID] == 0 {
|
||||
subnetworkID = wire.SubnetworkIDUnknown
|
||||
}
|
||||
|
||||
// Use a 50% chance for choosing between tried and new table entries.
|
||||
if a.nTried > 0 && (a.nNew == 0 || a.rand.Intn(2) == 0) {
|
||||
if a.nTried[subnetworkID] > 0 && (a.nNew[subnetworkID] == 0 || a.rand.Intn(2) == 0) {
|
||||
// Tried entry.
|
||||
large := 1 << 30
|
||||
factor := 1.0
|
||||
for {
|
||||
// pick a random bucket.
|
||||
bucket := a.rand.Intn(len(a.addrTried))
|
||||
if a.addrTried[bucket].Len() == 0 {
|
||||
bucket := a.rand.Intn(len(a.addrTried[subnetworkID]))
|
||||
if a.addrTried[subnetworkID][bucket].Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Pick a random entry in the list
|
||||
e := a.addrTried[bucket].Front()
|
||||
e := a.addrTried[subnetworkID][bucket].Front()
|
||||
for i :=
|
||||
a.rand.Int63n(int64(a.addrTried[bucket].Len())); i > 0; i-- {
|
||||
a.rand.Int63n(int64(a.addrTried[subnetworkID][bucket].Len())); i > 0; i-- {
|
||||
e = e.Next()
|
||||
}
|
||||
ka := e.Value.(*KnownAddress)
|
||||
@ -773,21 +853,21 @@ func (a *AddrManager) GetAddress() *KnownAddress {
|
||||
}
|
||||
factor *= 1.2
|
||||
}
|
||||
} else {
|
||||
} else if a.nNew[subnetworkID] > 0 {
|
||||
// new node.
|
||||
// XXX use a closure/function to avoid repeating this.
|
||||
large := 1 << 30
|
||||
factor := 1.0
|
||||
for {
|
||||
// Pick a random bucket.
|
||||
bucket := a.rand.Intn(len(a.addrNew))
|
||||
if len(a.addrNew[bucket]) == 0 {
|
||||
bucket := a.rand.Intn(len(a.addrNew[subnetworkID]))
|
||||
if len(a.addrNew[subnetworkID][bucket]) == 0 {
|
||||
continue
|
||||
}
|
||||
// Then, a random entry in it.
|
||||
var ka *KnownAddress
|
||||
nth := a.rand.Intn(len(a.addrNew[bucket]))
|
||||
for _, value := range a.addrNew[bucket] {
|
||||
nth := a.rand.Intn(len(a.addrNew[subnetworkID][bucket]))
|
||||
for _, value := range a.addrNew[subnetworkID][bucket] {
|
||||
if nth == 0 {
|
||||
ka = value
|
||||
}
|
||||
@ -802,6 +882,7 @@ func (a *AddrManager) GetAddress() *KnownAddress {
|
||||
factor *= 1.2
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AddrManager) find(addr *wire.NetAddress) *KnownAddress {
|
||||
@ -851,7 +932,7 @@ func (a *AddrManager) Connected(addr *wire.NetAddress) {
|
||||
// Good marks the given address as good. To be called after a successful
|
||||
// connection and version exchange. If the address is unknown to the address
|
||||
// manager it will be ignored.
|
||||
func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
func (a *AddrManager) Good(addr *wire.NetAddress, subnetworkID *subnetworkid.SubnetworkID) {
|
||||
a.mtx.Lock()
|
||||
defer a.mtx.Unlock()
|
||||
|
||||
@ -859,6 +940,7 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
if ka == nil {
|
||||
return
|
||||
}
|
||||
oldSubnetworkID := ka.subnetworkID
|
||||
|
||||
// ka.Timestamp is not updated here to avoid leaking information
|
||||
// about currently connected peers.
|
||||
@ -866,6 +948,7 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
ka.lastsuccess = now
|
||||
ka.lastattempt = now
|
||||
ka.attempts = 0
|
||||
ka.subnetworkID = subnetworkID
|
||||
|
||||
// move to tried set, optionally evicting other addresses if neeed.
|
||||
if ka.tried {
|
||||
@ -878,17 +961,17 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
// record one of the buckets in question and call it the `first'
|
||||
addrKey := NetAddressKey(addr)
|
||||
oldBucket := -1
|
||||
for i := range a.addrNew {
|
||||
for i := range a.addrNew[*oldSubnetworkID] {
|
||||
// we check for existence so we can record the first one
|
||||
if _, ok := a.addrNew[i][addrKey]; ok {
|
||||
delete(a.addrNew[i], addrKey)
|
||||
if _, ok := a.addrNew[*oldSubnetworkID][i][addrKey]; ok {
|
||||
delete(a.addrNew[*oldSubnetworkID][i], addrKey)
|
||||
ka.refs--
|
||||
if oldBucket == -1 {
|
||||
oldBucket = i
|
||||
}
|
||||
}
|
||||
}
|
||||
a.nNew--
|
||||
a.nNew[*oldSubnetworkID]--
|
||||
|
||||
if oldBucket == -1 {
|
||||
// What? wasn't in a bucket after all.... Panic?
|
||||
@ -898,15 +981,15 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
bucket := a.getTriedBucket(ka.na)
|
||||
|
||||
// Room in this tried bucket?
|
||||
if a.addrTried[bucket].Len() < triedBucketSize {
|
||||
if a.nTried[*ka.subnetworkID] == 0 || a.addrTried[*ka.subnetworkID][bucket].Len() < triedBucketSize {
|
||||
ka.tried = true
|
||||
a.addrTried[bucket].PushBack(ka)
|
||||
a.nTried++
|
||||
a.updateAddrTried(bucket, ka)
|
||||
a.nTried[*ka.subnetworkID]++
|
||||
return
|
||||
}
|
||||
|
||||
// No room, we have to evict something else.
|
||||
entry := a.pickTried(bucket)
|
||||
entry := a.pickTried(ka.subnetworkID, bucket)
|
||||
rmka := entry.Value.(*KnownAddress)
|
||||
|
||||
// First bucket it would have been put in.
|
||||
@ -914,7 +997,7 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
|
||||
// If no room in the original bucket, we put it in a bucket we just
|
||||
// freed up a space in.
|
||||
if len(a.addrNew[newBucket]) >= newBucketSize {
|
||||
if len(a.addrNew[*ka.subnetworkID][newBucket]) >= newBucketSize {
|
||||
newBucket = oldBucket
|
||||
}
|
||||
|
||||
@ -928,13 +1011,13 @@ func (a *AddrManager) Good(addr *wire.NetAddress) {
|
||||
// We don't touch a.nTried here since the number of tried stays the same
|
||||
// but we decemented new above, raise it again since we're putting
|
||||
// something back.
|
||||
a.nNew++
|
||||
a.nNew[*ka.subnetworkID]++
|
||||
|
||||
rmkey := NetAddressKey(rmka.na)
|
||||
log.Tracef("Replacing %s with %s in tried", rmkey, addrKey)
|
||||
|
||||
// We made sure there is space here just above.
|
||||
a.addrNew[newBucket][rmkey] = rmka
|
||||
a.addrNew[*ka.subnetworkID][newBucket][rmkey] = rmka
|
||||
}
|
||||
|
||||
// AddLocalAddress adds na to the list of known local addresses to advertise
|
||||
@ -1082,13 +1165,14 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
|
||||
|
||||
// New returns a new bitcoin address manager.
|
||||
// Use Start to begin processing asynchronous address updates.
|
||||
func New(dataDir string, lookupFunc func(string) ([]net.IP, error)) *AddrManager {
|
||||
func New(dataDir string, lookupFunc func(string) ([]net.IP, error), subnetworkID *subnetworkid.SubnetworkID) *AddrManager {
|
||||
am := AddrManager{
|
||||
peersFile: filepath.Join(dataDir, "peers.json"),
|
||||
lookupFunc: lookupFunc,
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
quit: make(chan struct{}),
|
||||
localAddresses: make(map[string]*localAddress),
|
||||
peersFile: filepath.Join(dataDir, "peers.json"),
|
||||
lookupFunc: lookupFunc,
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
quit: make(chan struct{}),
|
||||
localAddresses: make(map[string]*localAddress),
|
||||
localSubnetworkID: subnetworkID,
|
||||
}
|
||||
am.reset()
|
||||
return &am
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/daglabs/btcd/util/subnetworkid"
|
||||
|
||||
"github.com/daglabs/btcd/addrmgr"
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
@ -103,7 +105,7 @@ func lookupFunc(host string) ([]net.IP, error) {
|
||||
}
|
||||
|
||||
func TestStartStop(t *testing.T) {
|
||||
n := addrmgr.New("teststartstop", lookupFunc)
|
||||
n := addrmgr.New("teststartstop", lookupFunc, &wire.SubnetworkIDSupportsAll)
|
||||
n.Start()
|
||||
err := n.Stop()
|
||||
if err != nil {
|
||||
@ -136,7 +138,7 @@ func TestAddAddressByIP(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
amgr := addrmgr.New("testaddressbyip", nil)
|
||||
amgr := addrmgr.New("testaddressbyip", nil, &wire.SubnetworkIDSupportsAll)
|
||||
for i, test := range tests {
|
||||
err := amgr.AddAddressByIP(test.addrIP)
|
||||
if test.err != nil && err == nil {
|
||||
@ -192,7 +194,7 @@ func TestAddLocalAddress(t *testing.T) {
|
||||
true,
|
||||
},
|
||||
}
|
||||
amgr := addrmgr.New("testaddlocaladdress", nil)
|
||||
amgr := addrmgr.New("testaddlocaladdress", nil, &wire.SubnetworkIDSupportsAll)
|
||||
for x, test := range tests {
|
||||
result := amgr.AddLocalAddress(&test.address, test.priority)
|
||||
if result == nil && !test.valid {
|
||||
@ -209,7 +211,7 @@ func TestAddLocalAddress(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAttempt(t *testing.T) {
|
||||
n := addrmgr.New("testattempt", lookupFunc)
|
||||
n := addrmgr.New("testattempt", lookupFunc, &wire.SubnetworkIDSupportsAll)
|
||||
|
||||
// Add a new address and get it
|
||||
err := n.AddAddressByIP(someIP + ":8333")
|
||||
@ -231,7 +233,7 @@ func TestAttempt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConnected(t *testing.T) {
|
||||
n := addrmgr.New("testconnected", lookupFunc)
|
||||
n := addrmgr.New("testconnected", lookupFunc, &wire.SubnetworkIDSupportsAll)
|
||||
|
||||
// Add a new address and get it
|
||||
err := n.AddAddressByIP(someIP + ":8333")
|
||||
@ -251,7 +253,7 @@ func TestConnected(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNeedMoreAddresses(t *testing.T) {
|
||||
n := addrmgr.New("testneedmoreaddresses", lookupFunc)
|
||||
n := addrmgr.New("testneedmoreaddresses", lookupFunc, &wire.SubnetworkIDSupportsAll)
|
||||
addrsToAdd := 1500
|
||||
b := n.NeedMoreAddresses()
|
||||
if !b {
|
||||
@ -271,7 +273,7 @@ func TestNeedMoreAddresses(t *testing.T) {
|
||||
srcAddr := wire.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
numAddrs := n.NumAddresses()
|
||||
numAddrs := n.TotalNumAddresses()
|
||||
if numAddrs > addrsToAdd {
|
||||
t.Errorf("Number of addresses is too many %d vs %d", numAddrs, addrsToAdd)
|
||||
}
|
||||
@ -283,7 +285,7 @@ func TestNeedMoreAddresses(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGood(t *testing.T) {
|
||||
n := addrmgr.New("testgood", lookupFunc)
|
||||
n := addrmgr.New("testgood", lookupFunc, &wire.SubnetworkIDSupportsAll)
|
||||
addrsToAdd := 64 * 64
|
||||
addrs := make([]*wire.NetAddress, addrsToAdd)
|
||||
|
||||
@ -300,10 +302,10 @@ func TestGood(t *testing.T) {
|
||||
|
||||
n.AddAddresses(addrs, srcAddr)
|
||||
for _, addr := range addrs {
|
||||
n.Good(addr)
|
||||
n.Good(addr, &wire.SubnetworkIDSupportsAll)
|
||||
}
|
||||
|
||||
numAddrs := n.NumAddresses()
|
||||
numAddrs := n.TotalNumAddresses()
|
||||
if numAddrs >= addrsToAdd {
|
||||
t.Errorf("Number of addresses is too many: %d vs %d", numAddrs, addrsToAdd)
|
||||
}
|
||||
@ -315,7 +317,8 @@ func TestGood(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetAddress(t *testing.T) {
|
||||
n := addrmgr.New("testgetaddress", lookupFunc)
|
||||
localSubnetworkID := &subnetworkid.SubnetworkID{0xff}
|
||||
n := addrmgr.New("testgetaddress", lookupFunc, localSubnetworkID)
|
||||
|
||||
// Get an address from an empty set (should error)
|
||||
if rv := n.GetAddress(); rv != nil {
|
||||
@ -334,9 +337,12 @@ func TestGetAddress(t *testing.T) {
|
||||
if ka.NetAddress().IP.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
|
||||
}
|
||||
if *ka.SubnetworkID() != wire.SubnetworkIDUnknown {
|
||||
t.Errorf("Wrong Subnetwork ID: got %v, want %v", *ka.SubnetworkID(), wire.SubnetworkIDUnknown)
|
||||
}
|
||||
|
||||
// Mark this as a good address and get it
|
||||
n.Good(ka.NetAddress())
|
||||
n.Good(ka.NetAddress(), localSubnetworkID)
|
||||
ka = n.GetAddress()
|
||||
if ka == nil {
|
||||
t.Fatalf("Did not get an address where there is one in the pool")
|
||||
@ -344,8 +350,11 @@ func TestGetAddress(t *testing.T) {
|
||||
if ka.NetAddress().IP.String() != someIP {
|
||||
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
|
||||
}
|
||||
if *ka.SubnetworkID() != *localSubnetworkID {
|
||||
t.Errorf("Wrong Subnetwork ID: got %v, want %v", ka.SubnetworkID(), localSubnetworkID)
|
||||
}
|
||||
|
||||
numAddrs := n.NumAddresses()
|
||||
numAddrs := n.TotalNumAddresses()
|
||||
if numAddrs != 1 {
|
||||
t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1)
|
||||
}
|
||||
@ -401,7 +410,7 @@ func TestGetBestLocalAddress(t *testing.T) {
|
||||
*/
|
||||
}
|
||||
|
||||
amgr := addrmgr.New("testgetbestlocaladdress", nil)
|
||||
amgr := addrmgr.New("testgetbestlocaladdress", nil, &wire.SubnetworkIDSupportsAll)
|
||||
|
||||
// Test against default when there's no address
|
||||
for x, test := range tests {
|
||||
|
@ -7,19 +7,22 @@ package addrmgr
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/daglabs/btcd/util/subnetworkid"
|
||||
|
||||
"github.com/daglabs/btcd/wire"
|
||||
)
|
||||
|
||||
// KnownAddress tracks information about a known network address that is used
|
||||
// to determine how viable an address is.
|
||||
type KnownAddress struct {
|
||||
na *wire.NetAddress
|
||||
srcAddr *wire.NetAddress
|
||||
attempts int
|
||||
lastattempt time.Time
|
||||
lastsuccess time.Time
|
||||
tried bool
|
||||
refs int // reference count of new buckets
|
||||
na *wire.NetAddress
|
||||
srcAddr *wire.NetAddress
|
||||
attempts int
|
||||
lastattempt time.Time
|
||||
lastsuccess time.Time
|
||||
tried bool
|
||||
refs int // reference count of new buckets
|
||||
subnetworkID *subnetworkid.SubnetworkID
|
||||
}
|
||||
|
||||
// NetAddress returns the underlying wire.NetAddress associated with the
|
||||
@ -28,6 +31,11 @@ func (ka *KnownAddress) NetAddress() *wire.NetAddress {
|
||||
return ka.na
|
||||
}
|
||||
|
||||
// SubnetworkID returns the subnetwork ID of the known address.
|
||||
func (ka *KnownAddress) SubnetworkID() *subnetworkid.SubnetworkID {
|
||||
return ka.subnetworkID
|
||||
}
|
||||
|
||||
// LastAttempt returns the last time the known address was attempted.
|
||||
func (ka *KnownAddress) LastAttempt() time.Time {
|
||||
return ka.lastattempt
|
||||
|
@ -429,7 +429,7 @@ func (sp *Peer) OnVersion(_ *peer.Peer, msg *wire.MsgVersion) {
|
||||
}
|
||||
|
||||
// Mark the address as a known good address.
|
||||
addrManager.Good(sp.NA())
|
||||
addrManager.Good(sp.NA(), &msg.SubnetworkID)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2313,7 +2313,7 @@ func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params
|
||||
services &^= wire.SFNodeCF
|
||||
}
|
||||
|
||||
amgr := addrmgr.New(config.MainConfig().DataDir, serverutils.BTCDLookup)
|
||||
amgr := addrmgr.New(config.MainConfig().DataDir, serverutils.BTCDLookup, config.MainConfig().SubnetworkID)
|
||||
|
||||
var listeners []net.Listener
|
||||
var nat serverutils.NAT
|
||||
@ -2403,6 +2403,7 @@ func NewServer(listenAddrs []string, db database.DB, dagParams *dagconfig.Params
|
||||
TimeSource: s.TimeSource,
|
||||
SigCache: s.SigCache,
|
||||
IndexManager: indexManager,
|
||||
SubnetworkID: config.MainConfig().SubnetworkID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -119,6 +119,9 @@ var (
|
||||
|
||||
// SubnetworkIDRegistry is the subnetwork ID which is used for adding new sub networks to the registry
|
||||
SubnetworkIDRegistry = subnetworkid.SubnetworkID{2}
|
||||
|
||||
// SubnetworkIDUnknown is the subnetwork ID which is used for marking subnetwork ID as unknown in the adress manager
|
||||
SubnetworkIDUnknown = subnetworkid.SubnetworkID{3}
|
||||
)
|
||||
|
||||
// scriptFreeList defines a free list of byte slices (up to the maximum number
|
||||
|
Loading…
x
Reference in New Issue
Block a user