diff --git a/addrmgr/addrmanager.go b/addrmgr/addrmanager.go index 092ae00e6..87b0dbe74 100644 --- a/addrmgr/addrmanager.go +++ b/addrmgr/addrmanager.go @@ -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 diff --git a/addrmgr/addrmanager_test.go b/addrmgr/addrmanager_test.go index 861cf4d1b..42242ef01 100644 --- a/addrmgr/addrmanager_test.go +++ b/addrmgr/addrmanager_test.go @@ -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 { diff --git a/addrmgr/knownaddress.go b/addrmgr/knownaddress.go index 5dfbb9e9b..7fee3e31b 100644 --- a/addrmgr/knownaddress.go +++ b/addrmgr/knownaddress.go @@ -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 diff --git a/server/p2p/p2p.go b/server/p2p/p2p.go index c8ec15b06..1ce205347 100644 --- a/server/p2p/p2p.go +++ b/server/p2p/p2p.go @@ -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 diff --git a/wire/msgtx.go b/wire/msgtx.go index af66dce78..2dc82512f 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -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