2019-03-15 12:31:24 +03:00
|
|
|
package wtclient
|
|
|
|
|
|
|
|
import (
|
|
|
|
"container/list"
|
2019-06-08 03:45:27 +03:00
|
|
|
"net"
|
2019-03-15 12:31:24 +03:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/watchtower/wtdb"
|
|
|
|
)
|
|
|
|
|
|
|
|
// TowerCandidateIterator provides an abstraction for iterating through possible
|
|
|
|
// watchtower addresses when attempting to create a new session.
|
|
|
|
type TowerCandidateIterator interface {
|
2019-06-08 03:45:27 +03:00
|
|
|
// AddCandidate adds a new candidate tower to the iterator. If the
|
|
|
|
// candidate already exists, then any new addresses are added to it.
|
|
|
|
AddCandidate(*wtdb.Tower)
|
|
|
|
|
|
|
|
// RemoveCandidate removes an existing candidate tower from the
|
|
|
|
// iterator. An optional address can be provided to indicate a stale
|
|
|
|
// tower address to remove it. If it isn't provided, then the tower is
|
|
|
|
// completely removed from the iterator.
|
2020-11-03 21:27:57 +03:00
|
|
|
RemoveCandidate(wtdb.TowerID, net.Addr) error
|
2019-06-08 03:45:27 +03:00
|
|
|
|
|
|
|
// IsActive determines whether a given tower is exists within the
|
|
|
|
// iterator.
|
|
|
|
IsActive(wtdb.TowerID) bool
|
|
|
|
|
2019-03-15 12:31:24 +03:00
|
|
|
// Reset clears any internal iterator state, making previously taken
|
|
|
|
// candidates available as long as they remain in the set.
|
|
|
|
Reset() error
|
|
|
|
|
|
|
|
// Next returns the next candidate tower. The iterator is not required
|
|
|
|
// to return results in any particular order. If no more candidates are
|
|
|
|
// available, ErrTowerCandidatesExhausted is returned.
|
|
|
|
Next() (*wtdb.Tower, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// towerListIterator is a linked-list backed TowerCandidateIterator.
|
|
|
|
type towerListIterator struct {
|
|
|
|
mu sync.Mutex
|
2019-06-08 03:45:27 +03:00
|
|
|
queue *list.List
|
2019-03-15 12:31:24 +03:00
|
|
|
nextCandidate *list.Element
|
2019-06-08 03:45:27 +03:00
|
|
|
candidates map[wtdb.TowerID]*wtdb.Tower
|
2019-03-15 12:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compile-time constraint to ensure *towerListIterator implements the
|
|
|
|
// TowerCandidateIterator interface.
|
|
|
|
var _ TowerCandidateIterator = (*towerListIterator)(nil)
|
|
|
|
|
|
|
|
// newTowerListIterator initializes a new towerListIterator from a variadic list
|
|
|
|
// of lnwire.NetAddresses.
|
|
|
|
func newTowerListIterator(candidates ...*wtdb.Tower) *towerListIterator {
|
|
|
|
iter := &towerListIterator{
|
2019-06-08 03:45:27 +03:00
|
|
|
queue: list.New(),
|
|
|
|
candidates: make(map[wtdb.TowerID]*wtdb.Tower),
|
2019-03-15 12:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, candidate := range candidates {
|
2019-06-08 03:45:27 +03:00
|
|
|
iter.queue.PushBack(candidate.ID)
|
|
|
|
iter.candidates[candidate.ID] = candidate
|
2019-03-15 12:31:24 +03:00
|
|
|
}
|
|
|
|
iter.Reset()
|
|
|
|
|
|
|
|
return iter
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset clears the iterators state, and makes the address at the front of the
|
|
|
|
// list the next item to be returned..
|
|
|
|
func (t *towerListIterator) Reset() error {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
|
|
|
|
// Reset the next candidate to the front of the linked-list.
|
2019-06-08 03:45:27 +03:00
|
|
|
t.nextCandidate = t.queue.Front()
|
2019-03-15 12:31:24 +03:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Next returns the next candidate tower. This iterator will always return
|
|
|
|
// candidates in the order given when the iterator was instantiated. If no more
|
|
|
|
// candidates are available, ErrTowerCandidatesExhausted is returned.
|
|
|
|
func (t *towerListIterator) Next() (*wtdb.Tower, error) {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
|
2019-06-08 03:45:27 +03:00
|
|
|
for t.nextCandidate != nil {
|
|
|
|
// Propose the tower at the front of the list.
|
|
|
|
towerID := t.nextCandidate.Value.(wtdb.TowerID)
|
|
|
|
|
|
|
|
// Check whether this tower is still considered a candidate. If
|
|
|
|
// it's not, we'll proceed to the next.
|
|
|
|
tower, ok := t.candidates[towerID]
|
|
|
|
if !ok {
|
|
|
|
nextCandidate := t.nextCandidate.Next()
|
|
|
|
t.queue.Remove(t.nextCandidate)
|
|
|
|
t.nextCandidate = nextCandidate
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the next candidate to the subsequent element.
|
|
|
|
t.nextCandidate = t.nextCandidate.Next()
|
|
|
|
return tower, nil
|
2019-03-15 12:31:24 +03:00
|
|
|
}
|
|
|
|
|
2019-06-08 03:45:27 +03:00
|
|
|
return nil, ErrTowerCandidatesExhausted
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddCandidate adds a new candidate tower to the iterator. If the candidate
|
|
|
|
// already exists, then any new addresses are added to it.
|
|
|
|
func (t *towerListIterator) AddCandidate(candidate *wtdb.Tower) {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
2019-03-15 12:31:24 +03:00
|
|
|
|
2019-06-08 03:45:27 +03:00
|
|
|
if tower, ok := t.candidates[candidate.ID]; !ok {
|
|
|
|
t.queue.PushBack(candidate.ID)
|
|
|
|
t.candidates[candidate.ID] = candidate
|
|
|
|
|
|
|
|
// If we've reached the end of our queue, then this candidate
|
|
|
|
// will become the next.
|
|
|
|
if t.nextCandidate == nil {
|
|
|
|
t.nextCandidate = t.queue.Back()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for _, addr := range candidate.Addresses {
|
|
|
|
tower.AddAddress(addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveCandidate removes an existing candidate tower from the iterator. An
|
|
|
|
// optional address can be provided to indicate a stale tower address to remove
|
|
|
|
// it. If it isn't provided, then the tower is completely removed from the
|
|
|
|
// iterator.
|
2020-11-03 21:27:57 +03:00
|
|
|
func (t *towerListIterator) RemoveCandidate(candidate wtdb.TowerID,
|
|
|
|
addr net.Addr) error {
|
|
|
|
|
2019-06-08 03:45:27 +03:00
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
|
|
|
|
|
|
|
tower, ok := t.candidates[candidate]
|
|
|
|
if !ok {
|
2020-11-03 21:27:57 +03:00
|
|
|
return nil
|
2019-06-08 03:45:27 +03:00
|
|
|
}
|
|
|
|
if addr != nil {
|
|
|
|
tower.RemoveAddress(addr)
|
2020-11-03 21:27:57 +03:00
|
|
|
if len(tower.Addresses) == 0 {
|
|
|
|
return wtdb.ErrLastTowerAddr
|
|
|
|
}
|
2019-06-08 03:45:27 +03:00
|
|
|
} else {
|
|
|
|
delete(t.candidates, candidate)
|
|
|
|
}
|
2020-11-03 21:27:57 +03:00
|
|
|
|
|
|
|
return nil
|
2019-06-08 03:45:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsActive determines whether a given tower is exists within the iterator.
|
|
|
|
func (t *towerListIterator) IsActive(tower wtdb.TowerID) bool {
|
|
|
|
t.mu.Lock()
|
|
|
|
defer t.mu.Unlock()
|
2019-03-15 12:31:24 +03:00
|
|
|
|
2019-06-08 03:45:27 +03:00
|
|
|
_, ok := t.candidates[tower]
|
|
|
|
return ok
|
2019-03-15 12:31:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(conner): implement graph-backed candidate iterator for public towers.
|