github.com/decred/dcrlnd@v0.7.6/watchtower/wtclient/candidate_iterator.go (about) 1 package wtclient 2 3 import ( 4 "container/list" 5 "net" 6 "sync" 7 8 "github.com/decred/dcrlnd/watchtower/wtdb" 9 ) 10 11 // TowerCandidateIterator provides an abstraction for iterating through possible 12 // watchtower addresses when attempting to create a new session. 13 type TowerCandidateIterator interface { 14 // AddCandidate adds a new candidate tower to the iterator. If the 15 // candidate already exists, then any new addresses are added to it. 16 AddCandidate(*wtdb.Tower) 17 18 // RemoveCandidate removes an existing candidate tower from the 19 // iterator. An optional address can be provided to indicate a stale 20 // tower address to remove it. If it isn't provided, then the tower is 21 // completely removed from the iterator. 22 RemoveCandidate(wtdb.TowerID, net.Addr) error 23 24 // IsActive determines whether a given tower is exists within the 25 // iterator. 26 IsActive(wtdb.TowerID) bool 27 28 // Reset clears any internal iterator state, making previously taken 29 // candidates available as long as they remain in the set. 30 Reset() error 31 32 // Next returns the next candidate tower. The iterator is not required 33 // to return results in any particular order. If no more candidates are 34 // available, ErrTowerCandidatesExhausted is returned. 35 Next() (*wtdb.Tower, error) 36 } 37 38 // towerListIterator is a linked-list backed TowerCandidateIterator. 39 type towerListIterator struct { 40 mu sync.Mutex 41 queue *list.List 42 nextCandidate *list.Element 43 candidates map[wtdb.TowerID]*wtdb.Tower 44 } 45 46 // Compile-time constraint to ensure *towerListIterator implements the 47 // TowerCandidateIterator interface. 48 var _ TowerCandidateIterator = (*towerListIterator)(nil) 49 50 // newTowerListIterator initializes a new towerListIterator from a variadic list 51 // of lnwire.NetAddresses. 52 func newTowerListIterator(candidates ...*wtdb.Tower) *towerListIterator { 53 iter := &towerListIterator{ 54 queue: list.New(), 55 candidates: make(map[wtdb.TowerID]*wtdb.Tower), 56 } 57 58 for _, candidate := range candidates { 59 iter.queue.PushBack(candidate.ID) 60 iter.candidates[candidate.ID] = candidate 61 } 62 iter.Reset() 63 64 return iter 65 } 66 67 // Reset clears the iterators state, and makes the address at the front of the 68 // list the next item to be returned.. 69 func (t *towerListIterator) Reset() error { 70 t.mu.Lock() 71 defer t.mu.Unlock() 72 73 // Reset the next candidate to the front of the linked-list. 74 t.nextCandidate = t.queue.Front() 75 76 return nil 77 } 78 79 // Next returns the next candidate tower. This iterator will always return 80 // candidates in the order given when the iterator was instantiated. If no more 81 // candidates are available, ErrTowerCandidatesExhausted is returned. 82 func (t *towerListIterator) Next() (*wtdb.Tower, error) { 83 t.mu.Lock() 84 defer t.mu.Unlock() 85 86 for t.nextCandidate != nil { 87 // Propose the tower at the front of the list. 88 towerID := t.nextCandidate.Value.(wtdb.TowerID) 89 90 // Check whether this tower is still considered a candidate. If 91 // it's not, we'll proceed to the next. 92 tower, ok := t.candidates[towerID] 93 if !ok { 94 nextCandidate := t.nextCandidate.Next() 95 t.queue.Remove(t.nextCandidate) 96 t.nextCandidate = nextCandidate 97 continue 98 } 99 100 // Set the next candidate to the subsequent element. 101 t.nextCandidate = t.nextCandidate.Next() 102 return tower, nil 103 } 104 105 return nil, ErrTowerCandidatesExhausted 106 } 107 108 // AddCandidate adds a new candidate tower to the iterator. If the candidate 109 // already exists, then any new addresses are added to it. 110 func (t *towerListIterator) AddCandidate(candidate *wtdb.Tower) { 111 t.mu.Lock() 112 defer t.mu.Unlock() 113 114 if tower, ok := t.candidates[candidate.ID]; !ok { 115 t.queue.PushBack(candidate.ID) 116 t.candidates[candidate.ID] = candidate 117 118 // If we've reached the end of our queue, then this candidate 119 // will become the next. 120 if t.nextCandidate == nil { 121 t.nextCandidate = t.queue.Back() 122 } 123 } else { 124 for _, addr := range candidate.Addresses { 125 tower.AddAddress(addr) 126 } 127 } 128 } 129 130 // RemoveCandidate removes an existing candidate tower from the iterator. An 131 // optional address can be provided to indicate a stale tower address to remove 132 // it. If it isn't provided, then the tower is completely removed from the 133 // iterator. 134 func (t *towerListIterator) RemoveCandidate(candidate wtdb.TowerID, 135 addr net.Addr) error { 136 137 t.mu.Lock() 138 defer t.mu.Unlock() 139 140 tower, ok := t.candidates[candidate] 141 if !ok { 142 return nil 143 } 144 if addr != nil { 145 tower.RemoveAddress(addr) 146 if len(tower.Addresses) == 0 { 147 return wtdb.ErrLastTowerAddr 148 } 149 } else { 150 delete(t.candidates, candidate) 151 } 152 153 return nil 154 } 155 156 // IsActive determines whether a given tower is exists within the iterator. 157 func (t *towerListIterator) IsActive(tower wtdb.TowerID) bool { 158 t.mu.Lock() 159 defer t.mu.Unlock() 160 161 _, ok := t.candidates[tower] 162 return ok 163 } 164 165 // TODO(conner): implement graph-backed candidate iterator for public towers.