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 }