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

     1  package routing
     2  
     3  import (
     4  	"container/heap"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"time"
     9  
    10  	"github.com/decred/dcrlnd/channeldb"
    11  	"github.com/decred/dcrlnd/feature"
    12  	"github.com/decred/dcrlnd/lnwire"
    13  	"github.com/decred/dcrlnd/record"
    14  	"github.com/decred/dcrlnd/routing/route"
    15  	sphinx "github.com/decred/lightning-onion/v4"
    16  )
    17  
    18  const (
    19  	// infinity is used as a starting distance in our shortest path search.
    20  	infinity = math.MaxInt64
    21  
    22  	// RiskFactorBillionths controls the influence of time lock delta
    23  	// of a channel on route selection. It is expressed as billionths
    24  	// of mAt per mAt sent through the channel per time lock delta
    25  	// block. See edgeWeight function below for more details.
    26  	// The chosen value is based on the previous incorrect weight function
    27  	// 1 + timelock + fee * fee. In this function, the fee penalty
    28  	// diminishes the time lock penalty for all but the smallest amounts.
    29  	// To not change the behaviour of path finding too drastically, a
    30  	// relatively small value is chosen which is still big enough to give
    31  	// some effect with smaller time lock values. The value may need
    32  	// tweaking and/or be made configurable in the future.
    33  	RiskFactorBillionths = 15
    34  
    35  	// estimatedNodeCount is used to preallocate the path finding structures
    36  	// to avoid resizing and copies. It should be number on the same order as
    37  	// the number of active nodes in the network.
    38  	estimatedNodeCount = 10000
    39  )
    40  
    41  // pathFinder defines the interface of a path finding algorithm.
    42  type pathFinder = func(g *graphParams, r *RestrictParams,
    43  	cfg *PathFindingConfig, source, target route.Vertex,
    44  	amt lnwire.MilliAtom, finalHtlcExpiry int32) (
    45  	[]*channeldb.CachedEdgePolicy, error)
    46  
    47  var (
    48  	// DefaultAttemptCost is the default fixed virtual cost in path finding
    49  	// of a failed payment attempt. It is used to trade off potentially
    50  	// better routes against their probability of succeeding.
    51  	DefaultAttemptCost = lnwire.NewMAtomsFromAtoms(100)
    52  
    53  	// DefaultAttemptCostPPM is the default proportional virtual cost in
    54  	// path finding weight units of executing a payment attempt that fails.
    55  	// It is used to trade off potentially better routes against their
    56  	// probability of succeeding. This parameter is expressed in parts per
    57  	// million of the payment amount.
    58  	//
    59  	// It is impossible to pick a perfect default value. The current value
    60  	// of 0.1% is based on the idea that a transaction fee of 1% is within
    61  	// reasonable territory and that a payment shouldn't need more than 10
    62  	// attempts.
    63  	DefaultAttemptCostPPM = int64(1000)
    64  
    65  	// DefaultMinRouteProbability is the default minimum probability for routes
    66  	// returned from findPath.
    67  	DefaultMinRouteProbability = float64(0.01)
    68  
    69  	// DefaultAprioriHopProbability is the default a priori probability for
    70  	// a hop.
    71  	DefaultAprioriHopProbability = float64(0.6)
    72  )
    73  
    74  // edgePolicyWithSource is a helper struct to keep track of the source node
    75  // of a channel edge. ChannelEdgePolicy only contains to destination node
    76  // of the edge.
    77  type edgePolicyWithSource struct {
    78  	sourceNode route.Vertex
    79  	edge       *channeldb.CachedEdgePolicy
    80  }
    81  
    82  // finalHopParams encapsulates various parameters for route construction that
    83  // apply to the final hop in a route. These features include basic payment data
    84  // such as amounts and cltvs, as well as more complex features like destination
    85  // custom records and payment address.
    86  type finalHopParams struct {
    87  	amt         lnwire.MilliAtom
    88  	totalAmt    lnwire.MilliAtom
    89  	cltvDelta   uint16
    90  	records     record.CustomSet
    91  	paymentAddr *[32]byte
    92  }
    93  
    94  // newRoute constructs a route using the provided path and final hop constraints.
    95  // Any destination specific fields from the final hop params  will be attached
    96  // assuming the destination's feature vector signals support, otherwise this
    97  // method will fail.  If the route is too long, or the selected path cannot
    98  // support the fully payment including fees, then a non-nil error is returned.
    99  //
   100  // NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
   101  // the source to the target node of the path finding attempt. It is assumed
   102  // that any feature vectors on all hops have been validated for transitive
   103  // dependencies.
   104  func newRoute(sourceVertex route.Vertex,
   105  	pathEdges []*channeldb.CachedEdgePolicy, currentHeight uint32,
   106  	finalHop finalHopParams) (*route.Route, error) {
   107  
   108  	var (
   109  		hops []*route.Hop
   110  
   111  		// totalTimeLock will accumulate the cumulative time lock
   112  		// across the entire route. This value represents how long the
   113  		// sender will need to wait in the *worst* case.
   114  		totalTimeLock = currentHeight
   115  
   116  		// nextIncomingAmount is the amount that will need to flow into
   117  		// the *next* hop. Since we're going to be walking the route
   118  		// backwards below, this next hop gets closer and closer to the
   119  		// sender of the payment.
   120  		nextIncomingAmount lnwire.MilliAtom
   121  	)
   122  
   123  	pathLength := len(pathEdges)
   124  	for i := pathLength - 1; i >= 0; i-- {
   125  		// Now we'll start to calculate the items within the per-hop
   126  		// payload for the hop this edge is leading to.
   127  		edge := pathEdges[i]
   128  
   129  		// We'll calculate the amounts, timelocks, and fees for each
   130  		// hop in the route. The base case is the final hop which
   131  		// includes their amount and timelocks. These values will
   132  		// accumulate contributions from the preceding hops back to the
   133  		// sender as we compute the route in reverse.
   134  		var (
   135  			amtToForward     lnwire.MilliAtom
   136  			fee              lnwire.MilliAtom
   137  			outgoingTimeLock uint32
   138  			tlvPayload       bool
   139  			customRecords    record.CustomSet
   140  			mpp              *record.MPP
   141  		)
   142  
   143  		// Define a helper function that checks this edge's feature
   144  		// vector for support for a given feature. We assume at this
   145  		// point that the feature vectors transitive dependencies have
   146  		// been validated.
   147  		supports := func(feature lnwire.FeatureBit) bool {
   148  			// If this edge comes from router hints, the features
   149  			// could be nil.
   150  			if edge.ToNodeFeatures == nil {
   151  				return false
   152  			}
   153  			return edge.ToNodeFeatures.HasFeature(feature)
   154  		}
   155  
   156  		// We start by assuming the node doesn't support TLV. We'll now
   157  		// inspect the node's feature vector to see if we can promote
   158  		// the hop. We assume already that the feature vector's
   159  		// transitive dependencies have already been validated by path
   160  		// finding or some other means.
   161  		tlvPayload = supports(lnwire.TLVOnionPayloadOptional)
   162  
   163  		if i == len(pathEdges)-1 {
   164  			// If this is the last hop, then the hop payload will
   165  			// contain the exact amount. In BOLT #4: Onion Routing
   166  			// Protocol / "Payload for the Last Node", this is
   167  			// detailed.
   168  			amtToForward = finalHop.amt
   169  
   170  			// Fee is not part of the hop payload, but only used for
   171  			// reporting through RPC. Set to zero for the final hop.
   172  			fee = lnwire.MilliAtom(0)
   173  
   174  			// As this is the last hop, we'll use the specified
   175  			// final CLTV delta value instead of the value from the
   176  			// last link in the route.
   177  			totalTimeLock += uint32(finalHop.cltvDelta)
   178  			outgoingTimeLock = totalTimeLock
   179  
   180  			// Attach any custom records to the final hop if the
   181  			// receiver supports TLV.
   182  			if !tlvPayload && finalHop.records != nil {
   183  				return nil, errors.New("cannot attach " +
   184  					"custom records")
   185  			}
   186  			customRecords = finalHop.records
   187  
   188  			// If we're attaching a payment addr but the receiver
   189  			// doesn't support both TLV and payment addrs, fail.
   190  			payAddr := supports(lnwire.PaymentAddrOptional)
   191  			if !payAddr && finalHop.paymentAddr != nil {
   192  				return nil, errors.New("cannot attach " +
   193  					"payment addr")
   194  			}
   195  
   196  			// Otherwise attach the mpp record if it exists.
   197  			// TODO(halseth): move this to payment life cycle,
   198  			// where AMP options are set.
   199  			if finalHop.paymentAddr != nil {
   200  				mpp = record.NewMPP(
   201  					finalHop.totalAmt,
   202  					*finalHop.paymentAddr,
   203  				)
   204  			}
   205  		} else {
   206  			// The amount that the current hop needs to forward is
   207  			// equal to the incoming amount of the next hop.
   208  			amtToForward = nextIncomingAmount
   209  
   210  			// The fee that needs to be paid to the current hop is
   211  			// based on the amount that this hop needs to forward
   212  			// and its policy for the outgoing channel. This policy
   213  			// is stored as part of the incoming channel of
   214  			// the next hop.
   215  			fee = pathEdges[i+1].ComputeFee(amtToForward)
   216  
   217  			// We'll take the total timelock of the preceding hop as
   218  			// the outgoing timelock or this hop. Then we'll
   219  			// increment the total timelock incurred by this hop.
   220  			outgoingTimeLock = totalTimeLock
   221  			totalTimeLock += uint32(pathEdges[i+1].TimeLockDelta)
   222  		}
   223  
   224  		// Since we're traversing the path backwards atm, we prepend
   225  		// each new hop such that, the final slice of hops will be in
   226  		// the forwards order.
   227  		currentHop := &route.Hop{
   228  			PubKeyBytes:      edge.ToNodePubKey(),
   229  			ChannelID:        edge.ChannelID,
   230  			AmtToForward:     amtToForward,
   231  			OutgoingTimeLock: outgoingTimeLock,
   232  			LegacyPayload:    !tlvPayload,
   233  			CustomRecords:    customRecords,
   234  			MPP:              mpp,
   235  		}
   236  
   237  		hops = append([]*route.Hop{currentHop}, hops...)
   238  
   239  		// Finally, we update the amount that needs to flow into the
   240  		// *next* hop, which is the amount this hop needs to forward,
   241  		// accounting for the fee that it takes.
   242  		nextIncomingAmount = amtToForward + fee
   243  	}
   244  
   245  	// With the base routing data expressed as hops, build the full route
   246  	newRoute, err := route.NewRouteFromHops(
   247  		nextIncomingAmount, totalTimeLock, sourceVertex, hops,
   248  	)
   249  	if err != nil {
   250  		return nil, err
   251  	}
   252  
   253  	return newRoute, nil
   254  }
   255  
   256  // edgeWeight computes the weight of an edge. This value is used when searching
   257  // for the shortest path within the channel graph between two nodes. Weight is
   258  // is the fee itself plus a time lock penalty added to it. This benefits
   259  // channels with shorter time lock deltas and shorter (hops) routes in general.
   260  // RiskFactor controls the influence of time lock on route selection. This is
   261  // currently a fixed value, but might be configurable in the future.
   262  func edgeWeight(lockedAmt lnwire.MilliAtom, fee lnwire.MilliAtom,
   263  	timeLockDelta uint16) int64 {
   264  	// timeLockPenalty is the penalty for the time lock delta of this channel.
   265  	// It is controlled by RiskFactorBillionths and scales proportional
   266  	// to the amount that will pass through channel. Rationale is that it if
   267  	// a twice as large amount gets locked up, it is twice as bad.
   268  	timeLockPenalty := int64(lockedAmt) * int64(timeLockDelta) *
   269  		RiskFactorBillionths / 1000000000
   270  
   271  	return int64(fee) + timeLockPenalty
   272  }
   273  
   274  // graphParams wraps the set of graph parameters passed to findPath.
   275  type graphParams struct {
   276  	// graph is the ChannelGraph to be used during path finding.
   277  	graph routingGraph
   278  
   279  	// additionalEdges is an optional set of edges that should be
   280  	// considered during path finding, that is not already found in the
   281  	// channel graph.
   282  	additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy
   283  
   284  	// bandwidthHints is an interface that provides bandwidth hints that
   285  	// can provide a better estimate of the current channel bandwidth than
   286  	// what is found in the graph. It will override the capacities and
   287  	// disabled flags found in the graph for local channels when doing
   288  	// path finding if it has updated values for that channel. In
   289  	// particular, it should be set to the current available sending
   290  	// bandwidth for active local channels, and 0 for inactive channels.
   291  	bandwidthHints bandwidthHints
   292  }
   293  
   294  // RestrictParams wraps the set of restrictions passed to findPath that the
   295  // found path must adhere to.
   296  type RestrictParams struct {
   297  	// ProbabilitySource is a callback that is expected to return the
   298  	// success probability of traversing the channel from the node.
   299  	ProbabilitySource func(route.Vertex, route.Vertex,
   300  		lnwire.MilliAtom) float64
   301  
   302  	// FeeLimit is a maximum fee amount allowed to be used on the path from
   303  	// the source to the target.
   304  	FeeLimit lnwire.MilliAtom
   305  
   306  	// OutgoingChannelIDs is the list of channels that are allowed for the
   307  	// first hop. If nil, any channel may be used.
   308  	OutgoingChannelIDs []uint64
   309  
   310  	// LastHop is the pubkey of the last node before the final destination
   311  	// is reached. If nil, any node may be used.
   312  	LastHop *route.Vertex
   313  
   314  	// CltvLimit is the maximum time lock of the route excluding the final
   315  	// ctlv. After path finding is complete, the caller needs to increase
   316  	// all cltv expiry heights with the required final cltv delta.
   317  	CltvLimit uint32
   318  
   319  	// DestCustomRecords contains the custom records to drop off at the
   320  	// final hop, if any.
   321  	DestCustomRecords record.CustomSet
   322  
   323  	// DestFeatures is a feature vector describing what the final hop
   324  	// supports. If none are provided, pathfinding will try to inspect any
   325  	// features on the node announcement instead.
   326  	DestFeatures *lnwire.FeatureVector
   327  
   328  	// PaymentAddr is a random 32-byte value generated by the receiver to
   329  	// mitigate probing vectors and payment sniping attacks on overpaid
   330  	// invoices.
   331  	PaymentAddr *[32]byte
   332  }
   333  
   334  // PathFindingConfig defines global parameters that control the trade-off in
   335  // path finding between fees and probabiity.
   336  type PathFindingConfig struct {
   337  	// AttemptCost is the fixed virtual cost in path finding of a failed
   338  	// payment attempt. It is used to trade off potentially better routes
   339  	// against their probability of succeeding.
   340  	AttemptCost lnwire.MilliAtom
   341  
   342  	// AttemptCostPPM is the proportional virtual cost in path finding of a
   343  	// failed payment attempt. It is used to trade off potentially better
   344  	// routes against their probability of succeeding. This parameter is
   345  	// expressed in parts per million of the total payment amount.
   346  	AttemptCostPPM int64
   347  
   348  	// MinProbability defines the minimum success probability of the
   349  	// returned route.
   350  	MinProbability float64
   351  }
   352  
   353  // getOutgoingBalance returns the maximum available balance in any of the
   354  // channels of the given node. The second return parameters is the total
   355  // available balance.
   356  func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
   357  	bandwidthHints bandwidthHints,
   358  	g routingGraph) (lnwire.MilliAtom, lnwire.MilliAtom, error) {
   359  
   360  	var max, total lnwire.MilliAtom
   361  	cb := func(channel *channeldb.DirectedChannel) error {
   362  		if !channel.OutPolicySet {
   363  			return nil
   364  		}
   365  
   366  		chanID := channel.ChannelID
   367  
   368  		// Enforce outgoing channel restriction.
   369  		if outgoingChans != nil {
   370  			if _, ok := outgoingChans[chanID]; !ok {
   371  				return nil
   372  			}
   373  		}
   374  
   375  		bandwidth, ok := bandwidthHints.availableChanBandwidth(
   376  			chanID, 0,
   377  		)
   378  
   379  		// If the bandwidth is not available, use the channel capacity.
   380  		// This can happen when a channel is added to the graph after
   381  		// we've already queried the bandwidth hints.
   382  		if !ok {
   383  			bandwidth = lnwire.NewMAtomsFromAtoms(channel.Capacity)
   384  		}
   385  
   386  		if bandwidth > max {
   387  			max = bandwidth
   388  		}
   389  
   390  		total += bandwidth
   391  
   392  		return nil
   393  	}
   394  
   395  	// Iterate over all channels of the to node.
   396  	err := g.forEachNodeChannel(node, cb)
   397  	if err != nil {
   398  		return 0, 0, err
   399  	}
   400  	return max, total, err
   401  }
   402  
   403  // findPath attempts to find a path from the source node within the ChannelGraph
   404  // to the target node that's capable of supporting a payment of `amt` value. The
   405  // current approach implemented is modified version of Dijkstra's algorithm to
   406  // find a single shortest path between the source node and the destination. The
   407  // distance metric used for edges is related to the time-lock+fee costs along a
   408  // particular edge. If a path is found, this function returns a slice of
   409  // ChannelHop structs which encoded the chosen path from the target to the
   410  // source. The search is performed backwards from destination node back to
   411  // source. This is to properly accumulate fees that need to be paid along the
   412  // path and accurately check the amount to forward at every node against the
   413  // available bandwidth.
   414  func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
   415  	source, target route.Vertex, amt lnwire.MilliAtom,
   416  	finalHtlcExpiry int32) ([]*channeldb.CachedEdgePolicy, error) {
   417  
   418  	// Pathfinding can be a significant portion of the total payment
   419  	// latency, especially on low-powered devices. Log several metrics to
   420  	// aid in the analysis performance problems in this area.
   421  	start := time.Now()
   422  	nodesVisited := 0
   423  	edgesExpanded := 0
   424  	defer func() {
   425  		timeElapsed := time.Since(start)
   426  		log.Debugf("Pathfinding perf metrics: nodes=%v, edges=%v, "+
   427  			"time=%v", nodesVisited, edgesExpanded, timeElapsed)
   428  	}()
   429  
   430  	// If no destination features are provided, we will load what features
   431  	// we have for the target node from our graph.
   432  	features := r.DestFeatures
   433  	if features == nil {
   434  		var err error
   435  		features, err = g.graph.fetchNodeFeatures(target)
   436  		if err != nil {
   437  			return nil, err
   438  		}
   439  	}
   440  
   441  	// Ensure that the destination's features don't include unknown
   442  	// required features.
   443  	err := feature.ValidateRequired(features)
   444  	if err != nil {
   445  		log.Warnf("Pathfinding destination node features: %v", err)
   446  		return nil, errUnknownRequiredFeature
   447  	}
   448  
   449  	// Ensure that all transitive dependencies are set.
   450  	err = feature.ValidateDeps(features)
   451  	if err != nil {
   452  		log.Warnf("Pathfinding destination node features: %v", err)
   453  		return nil, errMissingDependentFeature
   454  	}
   455  
   456  	// Now that we know the feature vector is well formed, we'll proceed in
   457  	// checking that it supports the features we need, given our
   458  	// restrictions on the final hop.
   459  
   460  	// If the caller needs to send custom records, check that our
   461  	// destination feature vector supports TLV.
   462  	if len(r.DestCustomRecords) > 0 &&
   463  		!features.HasFeature(lnwire.TLVOnionPayloadOptional) {
   464  
   465  		return nil, errNoTlvPayload
   466  	}
   467  
   468  	// If the caller has a payment address to attach, check that our
   469  	// destination feature vector supports them.
   470  	if r.PaymentAddr != nil &&
   471  		!features.HasFeature(lnwire.PaymentAddrOptional) {
   472  
   473  		return nil, errNoPaymentAddr
   474  	}
   475  
   476  	// Set up outgoing channel map for quicker access.
   477  	var outgoingChanMap map[uint64]struct{}
   478  	if len(r.OutgoingChannelIDs) > 0 {
   479  		outgoingChanMap = make(map[uint64]struct{})
   480  		for _, outChan := range r.OutgoingChannelIDs {
   481  			outgoingChanMap[outChan] = struct{}{}
   482  		}
   483  	}
   484  
   485  	// If we are routing from ourselves, check that we have enough local
   486  	// balance available.
   487  	self := g.graph.sourceNode()
   488  
   489  	if source == self {
   490  		max, total, err := getOutgoingBalance(
   491  			self, outgoingChanMap, g.bandwidthHints, g.graph,
   492  		)
   493  		if err != nil {
   494  			return nil, err
   495  		}
   496  
   497  		// If the total outgoing balance isn't sufficient, it will be
   498  		// impossible to complete the payment.
   499  		if total < amt {
   500  			return nil, errInsufficientBalance
   501  		}
   502  
   503  		// If there is only not enough capacity on a single route, it
   504  		// may still be possible to complete the payment by splitting.
   505  		if max < amt {
   506  			return nil, errNoPathFound
   507  		}
   508  	}
   509  
   510  	// First we'll initialize an empty heap which'll help us to quickly
   511  	// locate the next edge we should visit next during our graph
   512  	// traversal.
   513  	nodeHeap := newDistanceHeap(estimatedNodeCount)
   514  
   515  	// Holds the current best distance for a given node.
   516  	distance := make(map[route.Vertex]*nodeWithDist, estimatedNodeCount)
   517  
   518  	additionalEdgesWithSrc := make(map[route.Vertex][]*edgePolicyWithSource)
   519  	for vertex, outgoingEdgePolicies := range g.additionalEdges {
   520  		// Build reverse lookup to find incoming edges. Needed because
   521  		// search is taken place from target to source.
   522  		for _, outgoingEdgePolicy := range outgoingEdgePolicies {
   523  			toVertex := outgoingEdgePolicy.ToNodePubKey()
   524  			incomingEdgePolicy := &edgePolicyWithSource{
   525  				sourceNode: vertex,
   526  				edge:       outgoingEdgePolicy,
   527  			}
   528  
   529  			additionalEdgesWithSrc[toVertex] =
   530  				append(additionalEdgesWithSrc[toVertex],
   531  					incomingEdgePolicy)
   532  		}
   533  	}
   534  
   535  	// Build a preliminary destination hop structure to obtain the payload
   536  	// size.
   537  	var mpp *record.MPP
   538  	if r.PaymentAddr != nil {
   539  		mpp = record.NewMPP(amt, *r.PaymentAddr)
   540  	}
   541  
   542  	finalHop := route.Hop{
   543  		AmtToForward:     amt,
   544  		OutgoingTimeLock: uint32(finalHtlcExpiry),
   545  		CustomRecords:    r.DestCustomRecords,
   546  		LegacyPayload: !features.HasFeature(
   547  			lnwire.TLVOnionPayloadOptional,
   548  		),
   549  		MPP: mpp,
   550  	}
   551  
   552  	// We can't always assume that the end destination is publicly
   553  	// advertised to the network so we'll manually include the target node.
   554  	// The target node charges no fee. Distance is set to 0, because this is
   555  	// the starting point of the graph traversal. We are searching backwards
   556  	// to get the fees first time right and correctly match channel
   557  	// bandwidth.
   558  	//
   559  	// Don't record the initial partial path in the distance map and reserve
   560  	// that key for the source key in the case we route to ourselves.
   561  	partialPath := &nodeWithDist{
   562  		dist:            0,
   563  		weight:          0,
   564  		node:            target,
   565  		amountToReceive: amt,
   566  		incomingCltv:    finalHtlcExpiry,
   567  		probability:     1,
   568  		routingInfoSize: finalHop.PayloadSize(0),
   569  	}
   570  
   571  	// Calculate the absolute cltv limit. Use uint64 to prevent an overflow
   572  	// if the cltv limit is MaxUint32.
   573  	absoluteCltvLimit := uint64(r.CltvLimit) + uint64(finalHtlcExpiry)
   574  
   575  	// Calculate the absolute attempt cost that is used for probability
   576  	// estimation.
   577  	absoluteAttemptCost := int64(cfg.AttemptCost) +
   578  		int64(amt)*cfg.AttemptCostPPM/1000000
   579  
   580  	log.Debugf("Pathfinding absolute attempt cost: %v atoms",
   581  		float64(absoluteAttemptCost)/1000)
   582  
   583  	// processEdge is a helper closure that will be used to make sure edges
   584  	// satisfy our specific requirements.
   585  	processEdge := func(fromVertex route.Vertex,
   586  		fromFeatures *lnwire.FeatureVector,
   587  		edge *channeldb.CachedEdgePolicy, toNodeDist *nodeWithDist) {
   588  
   589  		edgesExpanded++
   590  
   591  		// Calculate amount that the candidate node would have to send
   592  		// out.
   593  		amountToSend := toNodeDist.amountToReceive
   594  
   595  		// Request the success probability for this edge.
   596  		edgeProbability := r.ProbabilitySource(
   597  			fromVertex, toNodeDist.node, amountToSend,
   598  		)
   599  
   600  		log.Trace(newLogClosure(func() string {
   601  			return fmt.Sprintf("path finding probability: fromnode=%v,"+
   602  				" tonode=%v, amt=%v, probability=%v",
   603  				fromVertex, toNodeDist.node, amountToSend,
   604  				edgeProbability)
   605  		}))
   606  
   607  		// If the probability is zero, there is no point in trying.
   608  		if edgeProbability == 0 {
   609  			return
   610  		}
   611  
   612  		// Compute fee that fromVertex is charging. It is based on the
   613  		// amount that needs to be sent to the next node in the route.
   614  		//
   615  		// Source node has no predecessor to pay a fee. Therefore set
   616  		// fee to zero, because it should not be included in the fee
   617  		// limit check and edge weight.
   618  		//
   619  		// Also determine the time lock delta that will be added to the
   620  		// route if fromVertex is selected. If fromVertex is the source
   621  		// node, no additional timelock is required.
   622  		var fee lnwire.MilliAtom
   623  		var timeLockDelta uint16
   624  		if fromVertex != source {
   625  			fee = edge.ComputeFee(amountToSend)
   626  			timeLockDelta = edge.TimeLockDelta
   627  		}
   628  
   629  		incomingCltv := toNodeDist.incomingCltv + int32(timeLockDelta)
   630  
   631  		// Check that we are within our CLTV limit.
   632  		if uint64(incomingCltv) > absoluteCltvLimit {
   633  			return
   634  		}
   635  
   636  		// amountToReceive is the amount that the node that is added to
   637  		// the distance map needs to receive from a (to be found)
   638  		// previous node in the route. That previous node will need to
   639  		// pay the amount that this node forwards plus the fee it
   640  		// charges.
   641  		amountToReceive := amountToSend + fee
   642  
   643  		// Check if accumulated fees would exceed fee limit when this
   644  		// node would be added to the path.
   645  		totalFee := amountToReceive - amt
   646  		if totalFee > r.FeeLimit {
   647  			return
   648  		}
   649  
   650  		// Calculate total probability of successfully reaching target
   651  		// by multiplying the probabilities. Both this edge and the rest
   652  		// of the route must succeed.
   653  		probability := toNodeDist.probability * edgeProbability
   654  
   655  		// If the probability is below the specified lower bound, we can
   656  		// abandon this direction. Adding further nodes can only lower
   657  		// the probability more.
   658  		if probability < cfg.MinProbability {
   659  			return
   660  		}
   661  
   662  		// By adding fromVertex in the route, there will be an extra
   663  		// weight composed of the fee that this node will charge and
   664  		// the amount that will be locked for timeLockDelta blocks in
   665  		// the HTLC that is handed out to fromVertex.
   666  		weight := edgeWeight(amountToReceive, fee, timeLockDelta)
   667  
   668  		// Compute the tentative weight to this new channel/edge
   669  		// which is the weight from our toNode to the target node
   670  		// plus the weight of this edge.
   671  		tempWeight := toNodeDist.weight + weight
   672  
   673  		// Add an extra factor to the weight to take into account the
   674  		// probability.
   675  		tempDist := getProbabilityBasedDist(
   676  			tempWeight, probability,
   677  			absoluteAttemptCost,
   678  		)
   679  
   680  		// If there is already a best route stored, compare this
   681  		// candidate route with the best route so far.
   682  		current, ok := distance[fromVertex]
   683  		if ok {
   684  			// If this route is worse than what we already found,
   685  			// skip this route.
   686  			if tempDist > current.dist {
   687  				return
   688  			}
   689  
   690  			// If the route is equally good and the probability
   691  			// isn't better, skip this route. It is important to
   692  			// also return if both cost and probability are equal,
   693  			// because otherwise the algorithm could run into an
   694  			// endless loop.
   695  			probNotBetter := probability <= current.probability
   696  			if tempDist == current.dist && probNotBetter {
   697  				return
   698  			}
   699  		}
   700  
   701  		// Every edge should have a positive time lock delta. If we
   702  		// encounter a zero delta, log a warning line.
   703  		if edge.TimeLockDelta == 0 {
   704  			log.Warnf("Channel %v has zero cltv delta",
   705  				edge.ChannelID)
   706  		}
   707  
   708  		// Calculate the total routing info size if this hop were to be
   709  		// included. If we are coming from the source hop, the payload
   710  		// size is zero, because the original htlc isn't in the onion
   711  		// blob.
   712  		var payloadSize uint64
   713  		if fromVertex != source {
   714  			supportsTlv := fromFeatures.HasFeature(
   715  				lnwire.TLVOnionPayloadOptional,
   716  			)
   717  
   718  			hop := route.Hop{
   719  				AmtToForward: amountToSend,
   720  				OutgoingTimeLock: uint32(
   721  					toNodeDist.incomingCltv,
   722  				),
   723  				LegacyPayload: !supportsTlv,
   724  			}
   725  
   726  			payloadSize = hop.PayloadSize(edge.ChannelID)
   727  		}
   728  
   729  		routingInfoSize := toNodeDist.routingInfoSize + payloadSize
   730  
   731  		// Skip paths that would exceed the maximum routing info size.
   732  		if routingInfoSize > sphinx.MaxPayloadSize {
   733  			return
   734  		}
   735  
   736  		// All conditions are met and this new tentative distance is
   737  		// better than the current best known distance to this node.
   738  		// The new better distance is recorded, and also our "next hop"
   739  		// map is populated with this edge.
   740  		withDist := &nodeWithDist{
   741  			dist:            tempDist,
   742  			weight:          tempWeight,
   743  			node:            fromVertex,
   744  			amountToReceive: amountToReceive,
   745  			incomingCltv:    incomingCltv,
   746  			probability:     probability,
   747  			nextHop:         edge,
   748  			routingInfoSize: routingInfoSize,
   749  		}
   750  		distance[fromVertex] = withDist
   751  
   752  		// Either push withDist onto the heap if the node
   753  		// represented by fromVertex is not already on the heap OR adjust
   754  		// its position within the heap via heap.Fix.
   755  		nodeHeap.PushOrFix(withDist)
   756  	}
   757  
   758  	// TODO(roasbeef): also add path caching
   759  	//  * similar to route caching, but doesn't factor in the amount
   760  
   761  	// Cache features because we visit nodes multiple times.
   762  	featureCache := make(map[route.Vertex]*lnwire.FeatureVector)
   763  
   764  	// getGraphFeatures returns (cached) node features from the graph.
   765  	getGraphFeatures := func(node route.Vertex) (*lnwire.FeatureVector,
   766  		error) {
   767  
   768  		// Check cache for features of the fromNode.
   769  		fromFeatures, ok := featureCache[node]
   770  		if ok {
   771  			return fromFeatures, nil
   772  		}
   773  
   774  		// Fetch node features fresh from the graph.
   775  		fromFeatures, err := g.graph.fetchNodeFeatures(node)
   776  		if err != nil {
   777  			return nil, err
   778  		}
   779  
   780  		// Don't route through nodes that contain unknown required
   781  		// features and mark as nil in the cache.
   782  		err = feature.ValidateRequired(fromFeatures)
   783  		if err != nil {
   784  			featureCache[node] = nil
   785  			return nil, nil
   786  		}
   787  
   788  		// Don't route through nodes that don't properly set all
   789  		// transitive feature dependencies and mark as nil in the cache.
   790  		err = feature.ValidateDeps(fromFeatures)
   791  		if err != nil {
   792  			featureCache[node] = nil
   793  			return nil, nil
   794  		}
   795  
   796  		// Update cache.
   797  		featureCache[node] = fromFeatures
   798  
   799  		return fromFeatures, nil
   800  	}
   801  
   802  	routeToSelf := source == target
   803  	for {
   804  		nodesVisited++
   805  
   806  		pivot := partialPath.node
   807  
   808  		// Create unified policies for all incoming connections.
   809  		u := newUnifiedPolicies(self, pivot, outgoingChanMap)
   810  
   811  		err := u.addGraphPolicies(g.graph)
   812  		if err != nil {
   813  			return nil, err
   814  		}
   815  
   816  		for _, reverseEdge := range additionalEdgesWithSrc[pivot] {
   817  			u.addPolicy(reverseEdge.sourceNode, reverseEdge.edge, 0)
   818  		}
   819  
   820  		amtToSend := partialPath.amountToReceive
   821  
   822  		// Expand all connections using the optimal policy for each
   823  		// connection.
   824  		for fromNode, unifiedPolicy := range u.policies {
   825  			// The target node is not recorded in the distance map.
   826  			// Therefore we need to have this check to prevent
   827  			// creating a cycle. Only when we intend to route to
   828  			// self, we allow this cycle to form. In that case we'll
   829  			// also break out of the search loop below.
   830  			if !routeToSelf && fromNode == target {
   831  				continue
   832  			}
   833  
   834  			// Apply last hop restriction if set.
   835  			if r.LastHop != nil &&
   836  				pivot == target && fromNode != *r.LastHop {
   837  
   838  				continue
   839  			}
   840  
   841  			policy := unifiedPolicy.getPolicy(
   842  				amtToSend, g.bandwidthHints,
   843  			)
   844  
   845  			if policy == nil {
   846  				continue
   847  			}
   848  
   849  			// Get feature vector for fromNode.
   850  			fromFeatures, err := getGraphFeatures(fromNode)
   851  			if err != nil {
   852  				return nil, err
   853  			}
   854  
   855  			// If there are no valid features, skip this node.
   856  			if fromFeatures == nil {
   857  				continue
   858  			}
   859  
   860  			// Check if this candidate node is better than what we
   861  			// already have.
   862  			processEdge(fromNode, fromFeatures, policy, partialPath)
   863  		}
   864  
   865  		if nodeHeap.Len() == 0 {
   866  			break
   867  		}
   868  
   869  		// Fetch the node within the smallest distance from our source
   870  		// from the heap.
   871  		partialPath = heap.Pop(&nodeHeap).(*nodeWithDist)
   872  
   873  		// If we've reached our source (or we don't have any incoming
   874  		// edges), then we're done here and can exit the graph
   875  		// traversal early.
   876  		if partialPath.node == source {
   877  			break
   878  		}
   879  	}
   880  
   881  	// Use the distance map to unravel the forward path from source to
   882  	// target.
   883  	var pathEdges []*channeldb.CachedEdgePolicy
   884  	currentNode := source
   885  	for {
   886  		// Determine the next hop forward using the next map.
   887  		currentNodeWithDist, ok := distance[currentNode]
   888  		if !ok {
   889  			// If the node doesn't have a next hop it means we
   890  			// didn't find a path.
   891  			return nil, errNoPathFound
   892  		}
   893  
   894  		// Add the next hop to the list of path edges.
   895  		pathEdges = append(pathEdges, currentNodeWithDist.nextHop)
   896  
   897  		// Advance current node.
   898  		currentNode = currentNodeWithDist.nextHop.ToNodePubKey()
   899  
   900  		// Check stop condition at the end of this loop. This prevents
   901  		// breaking out too soon for self-payments that have target set
   902  		// to source.
   903  		if currentNode == target {
   904  			break
   905  		}
   906  	}
   907  
   908  	// For the final hop, we'll set the node features to those determined
   909  	// above. These are either taken from the destination features, e.g.
   910  	// virtual or invoice features, or loaded as a fallback from the graph.
   911  	// The transitive dependencies were already validated above, so no need
   912  	// to do so now.
   913  	//
   914  	// NOTE: This may overwrite features loaded from the graph if
   915  	// destination features were provided. This is fine though, since our
   916  	// route construction does not care where the features are actually
   917  	// taken from. In the future we may wish to do route construction within
   918  	// findPath, and avoid using ChannelEdgePolicy altogether.
   919  	pathEdges[len(pathEdges)-1].ToNodeFeatures = features
   920  
   921  	log.Debugf("Found route: probability=%v, hops=%v, fee=%v",
   922  		distance[source].probability, len(pathEdges),
   923  		distance[source].amountToReceive-amt)
   924  
   925  	return pathEdges, nil
   926  }
   927  
   928  // getProbabilityBasedDist converts a weight into a distance that takes into
   929  // account the success probability and the (virtual) cost of a failed payment
   930  // attempt.
   931  //
   932  // Derivation:
   933  //
   934  // Suppose there are two routes A and B with fees Fa and Fb and success
   935  // probabilities Pa and Pb.
   936  //
   937  // Is the expected cost of trying route A first and then B lower than trying the
   938  // other way around?
   939  //
   940  // The expected cost of A-then-B is: Pa*Fa + (1-Pa)*Pb*(c+Fb)
   941  //
   942  // The expected cost of B-then-A is: Pb*Fb + (1-Pb)*Pa*(c+Fa)
   943  //
   944  // In these equations, the term representing the case where both A and B fail is
   945  // left out because its value would be the same in both cases.
   946  //
   947  // Pa*Fa + (1-Pa)*Pb*(c+Fb) < Pb*Fb + (1-Pb)*Pa*(c+Fa)
   948  //
   949  // Pa*Fa + Pb*c + Pb*Fb - Pa*Pb*c - Pa*Pb*Fb < Pb*Fb + Pa*c + Pa*Fa - Pa*Pb*c - Pa*Pb*Fa
   950  //
   951  // Removing terms that cancel out:
   952  // Pb*c - Pa*Pb*Fb < Pa*c - Pa*Pb*Fa
   953  //
   954  // Divide by Pa*Pb:
   955  // c/Pa - Fb < c/Pb - Fa
   956  //
   957  // Move terms around:
   958  // Fa + c/Pa < Fb + c/Pb
   959  //
   960  // So the value of F + c/P can be used to compare routes.
   961  func getProbabilityBasedDist(weight int64, probability float64, penalty int64) int64 {
   962  	// Clamp probability to prevent overflow.
   963  	const minProbability = 0.00001
   964  
   965  	if probability < minProbability {
   966  		return infinity
   967  	}
   968  
   969  	return weight + int64(float64(penalty)/probability)
   970  }