github.com/decred/dcrlnd@v0.7.6/routing/bandwidth.go (about)

     1  package routing
     2  
     3  import (
     4  	"github.com/decred/dcrlnd/channeldb"
     5  	"github.com/decred/dcrlnd/htlcswitch"
     6  	"github.com/decred/dcrlnd/lnwire"
     7  	"github.com/decred/dcrlnd/routing/route"
     8  )
     9  
    10  // bandwidthHints provides hints about the currently available balance in our
    11  // channels.
    12  type bandwidthHints interface {
    13  	// availableChanBandwidth returns the total available bandwidth for a
    14  	// channel and a bool indicating whether the channel hint was found.
    15  	// The amount parameter is used to validate the outgoing htlc amount
    16  	// that we wish to add to the channel against its flow restrictions. If
    17  	// a zero amount is provided, the minimum htlc value for the channel
    18  	// will be used. If the channel is unavailable, a zero amount is
    19  	// returned.
    20  	availableChanBandwidth(channelID uint64,
    21  		amount lnwire.MilliAtom) (lnwire.MilliAtom, bool)
    22  }
    23  
    24  // getLinkQuery is the function signature used to lookup a link.
    25  type getLinkQuery func(lnwire.ShortChannelID) (
    26  	htlcswitch.ChannelLink, error)
    27  
    28  // bandwidthManager is an implementation of the bandwidthHints interface which
    29  // uses the link lookup provided to query the link for our latest local channel
    30  // balances.
    31  type bandwidthManager struct {
    32  	getLink    getLinkQuery
    33  	localChans map[lnwire.ShortChannelID]struct{}
    34  }
    35  
    36  // newBandwidthManager creates a bandwidth manager for the source node provided
    37  // which is used to obtain hints from the lower layer w.r.t the available
    38  // bandwidth of edges on the network. Currently, we'll only obtain bandwidth
    39  // hints for the edges we directly have open ourselves. Obtaining these hints
    40  // allows us to reduce the number of extraneous attempts as we can skip channels
    41  // that are inactive, or just don't have enough bandwidth to carry the payment.
    42  func newBandwidthManager(graph routingGraph, sourceNode route.Vertex,
    43  	linkQuery getLinkQuery) (*bandwidthManager, error) {
    44  
    45  	manager := &bandwidthManager{
    46  		getLink:    linkQuery,
    47  		localChans: make(map[lnwire.ShortChannelID]struct{}),
    48  	}
    49  
    50  	// First, we'll collect the set of outbound edges from the target
    51  	// source node and add them to our bandwidth manager's map of channels.
    52  	err := graph.forEachNodeChannel(sourceNode,
    53  		func(channel *channeldb.DirectedChannel) error {
    54  			shortID := lnwire.NewShortChanIDFromInt(
    55  				channel.ChannelID,
    56  			)
    57  			manager.localChans[shortID] = struct{}{}
    58  
    59  			return nil
    60  		})
    61  
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  
    66  	return manager, nil
    67  }
    68  
    69  // getBandwidth queries the current state of a link and gets its currently
    70  // available bandwidth. Note that this function assumes that the channel being
    71  // queried is one of our local channels, so any failure to retrieve the link
    72  // is interpreted as the link being offline.
    73  func (b *bandwidthManager) getBandwidth(cid lnwire.ShortChannelID,
    74  	amount lnwire.MilliAtom) lnwire.MilliAtom {
    75  	link, err := b.getLink(cid)
    76  	if err != nil {
    77  		// If the link isn't online, then we'll report that it has
    78  		// zero bandwidth.
    79  		return 0
    80  	}
    81  
    82  	// If the link is found within the switch, but it isn't yet eligible
    83  	// to forward any HTLCs, then we'll treat it as if it isn't online in
    84  	// the first place.
    85  	if !link.EligibleToForward() {
    86  		return 0
    87  	}
    88  
    89  	// If our link isn't currently in a state where it can  add another
    90  	// outgoing htlc, treat the link as unusable.
    91  	if err := link.MayAddOutgoingHtlc(amount); err != nil {
    92  		return 0
    93  	}
    94  
    95  	// Otherwise, we'll return the current best estimate for the available
    96  	// bandwidth for the link.
    97  	return link.Bandwidth()
    98  }
    99  
   100  // availableChanBandwidth returns the total available bandwidth for a channel
   101  // and a bool indicating whether the channel hint was found. If the channel is
   102  // unavailable, a zero amount is returned.
   103  func (b *bandwidthManager) availableChanBandwidth(channelID uint64,
   104  	amount lnwire.MilliAtom) (lnwire.MilliAtom, bool) {
   105  
   106  	shortID := lnwire.NewShortChanIDFromInt(channelID)
   107  	_, ok := b.localChans[shortID]
   108  	if !ok {
   109  		return 0, false
   110  	}
   111  
   112  	return b.getBandwidth(shortID, amount), true
   113  }