diff --git a/packages/autopeering/selection/common.go b/packages/autopeering/selection/common.go index 679de027c35d405f3a0faf24686c5007cf3b03a1..60bff450316a9d78789e0a9d1bd027fabc4cd3b8 100644 --- a/packages/autopeering/selection/common.go +++ b/packages/autopeering/selection/common.go @@ -3,7 +3,7 @@ package selection import ( "time" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" + "github.com/iotaledger/goshimmer/packages/autopeering/peer" "github.com/iotaledger/hive.go/logger" ) @@ -30,10 +30,22 @@ type Config struct { Log *logger.Logger // These settings are optional: - DropOnUpdate bool // set true to drop all neighbors when the salt is updated - RequiredServices []service.Key // services required in order to select/be selected during autopeering + DropOnUpdate bool // set true to drop all neighbors when the salt is updated + NeighborValidator Validator // potential neighbor validator } +// A Validator checks whether a peer is a valid neighbor +type Validator interface { + IsValid(*peer.Peer) bool +} + +// The ValidatorFunc type is an adapter to allow the use of ordinary functions as neighbor validators. +// If f is a function with the appropriate signature, ValidatorFunc(f) is a Validator that calls f. +type ValidatorFunc func(p *peer.Peer) bool + +// IsValid calls f(p). +func (f ValidatorFunc) IsValid(p *peer.Peer) bool { return f(p) } + // Parameters holds the parameters that can be configured. type Parameters struct { InboundNeighborSize int // number of inbound neighbors diff --git a/packages/autopeering/selection/manager.go b/packages/autopeering/selection/manager.go index ae58fd5359dbb7282568356e14edcc064dfdfa6f..313d3c7cf2014d0338237b64476cc97dd592c0cf 100644 --- a/packages/autopeering/selection/manager.go +++ b/packages/autopeering/selection/manager.go @@ -5,7 +5,6 @@ import ( "time" "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/packages/autopeering/salt" "github.com/iotaledger/hive.go/logger" ) @@ -35,8 +34,8 @@ type manager struct { net network getPeersToConnect func() []*peer.Peer log *logger.Logger - dropOnUpdate bool // set true to drop all neighbors when the salt is updated - requiredServices []service.Key // services required in order to select/be selected during autopeering + dropOnUpdate bool // set true to drop all neighbors when the salt is updated + neighborValidator Validator // potential neighbor validator inbound *Neighborhood outbound *Neighborhood @@ -57,7 +56,7 @@ func newManager(net network, peersFunc func() []*peer.Peer, log *logger.Logger, getPeersToConnect: peersFunc, log: log, dropOnUpdate: config.DropOnUpdate, - requiredServices: config.RequiredServices, + neighborValidator: config.NeighborValidator, inbound: NewNeighborhood(inboundNeighborSize), outbound: NewNeighborhood(outboundNeighborSize), rejectionFilter: NewFilter(), @@ -217,9 +216,12 @@ func (m *manager) updateOutbound(resultChan chan<- peer.PeerDistance) { // filter out current neighbors filter := m.getConnectedFilter() - filteredList := filter.Apply(distList) + if m.neighborValidator != nil { + filter.AddCondition(m.neighborValidator.IsValid) + } + // filter out previous rejections - filteredList = m.rejectionFilter.Apply(filteredList) + filteredList := m.rejectionFilter.Apply(filter.Apply(distList)) if len(filteredList) == 0 { return } @@ -340,13 +342,10 @@ func (m *manager) isValidNeighbor(p *peer.Peer) bool { if m.getID() == p.ID() { return false } - // reject if required services are missing - for _, reqService := range m.requiredServices { - if p.Services().Get(reqService) == nil { - return false - } + if m.neighborValidator == nil { + return true } - return true + return m.neighborValidator.IsValid(p) } func (m *manager) triggerPeeringEvent(isOut bool, p *peer.Peer, status bool) { diff --git a/packages/autopeering/selection/selection.go b/packages/autopeering/selection/selection.go index 66183f246131b9d9cf9cdad18888166f44d346a3..f27393accdef5d234c46c4f7de5b092c5a11e21f 100644 --- a/packages/autopeering/selection/selection.go +++ b/packages/autopeering/selection/selection.go @@ -11,8 +11,9 @@ type Selector interface { } type Filter struct { - internal map[peer.ID]bool - lock sync.RWMutex + internal map[peer.ID]bool + conditions []func(*peer.Peer) bool + lock sync.RWMutex } func NewFilter() *Filter { @@ -21,29 +22,23 @@ func NewFilter() *Filter { } } -func (f *Filter) Apply(list []peer.PeerDistance) (filteredList []peer.PeerDistance) { +func (f *Filter) Apply(list []peer.PeerDistance) (filtered []peer.PeerDistance) { f.lock.RLock() defer f.lock.RUnlock() - for _, p := range list { - if !f.internal[p.Remote.ID()] { - filteredList = append(filteredList, p) - } - } - return filteredList -} -func (f *Filter) PushBack(list []peer.PeerDistance) (filteredList []peer.PeerDistance) { - var head, tail []peer.PeerDistance - f.lock.RLock() - defer f.lock.RUnlock() +list: for _, p := range list { - if f.internal[p.Remote.ID()] { - tail = append(tail, p) - } else { - head = append(head, p) + if _, contains := f.internal[p.Remote.ID()]; contains { + continue + } + for _, c := range f.conditions { + if !c(p.Remote) { + continue list + } } + filtered = append(filtered, p) } - return append(head, tail...) + return } func (f *Filter) AddPeers(n []*peer.Peer) { @@ -66,6 +61,10 @@ func (f *Filter) RemovePeer(peer peer.ID) { f.lock.Unlock() } +func (f *Filter) AddCondition(c func(p *peer.Peer) bool) { + f.conditions = append(f.conditions, c) +} + func (f *Filter) Clean() { f.lock.Lock() f.internal = make(map[peer.ID]bool) diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go index 5f2ee94b647742cbe55176316f9bb483a693adb7..2d578fc9fbbe41bf8ed96bc74fd24ba0a76febe4 100644 --- a/plugins/autopeering/autopeering.go +++ b/plugins/autopeering/autopeering.go @@ -48,12 +48,32 @@ func configureAP() { // enable peer selection only when gossip is enabled if !node.IsSkipped(gossip.PLUGIN) { Selection = selection.New(local.GetInstance(), Discovery, selection.Config{ - Log: log.Named("sel"), - RequiredServices: []service.Key{service.GossipKey}, + Log: log.Named("sel"), + NeighborValidator: selection.ValidatorFunc(isValidNeighbor), }) } } +// isValidNeighbor checks whether a peer is a valid neighbor. +func isValidNeighbor(p *peer.Peer) bool { + // gossip must be supported + gossipAddr := p.Services().Get(service.GossipKey) + if gossipAddr == nil { + return false + } + // the host for the gossip and peering service must be identical + gossipHost, _, err := net.SplitHostPort(gossipAddr.String()) + if err != nil { + return false + } + peeringAddr := p.Services().Get(service.PeeringKey) + peeringHost, _, err := net.SplitHostPort(peeringAddr.String()) + if err != nil { + return false + } + return gossipHost == peeringHost +} + func start(shutdownSignal <-chan struct{}) { defer log.Info("Stopping " + name + " ... done")