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.