diff --git a/routing/pathfind.go b/routing/pathfind.go index 1e442d43..2178941d 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -116,6 +116,41 @@ type Route struct { Hops []*Hop } +// sortableRoutes is a slice of routes that can be sorted. Routes are typically +// sorted according to their total cumulative fee within the route. In the case +// that two routes require and identical amount of fees, then the total +// time-lock will be used as the tie breaker. +type sortableRoutes []*Route + +// Len returns the number of routes in the collection. +// +// NOTE: This is part of the sort.Interface implementation. +func (s sortableRoutes) Len() int { + return len(s) +} + +// Less reports whether the route with index i should sort before the route +// with index j. To make this decision we first check if the total fees +// required for both routes are equal. If so, then we'll let the total time +// lock be the tie breaker. Otherwise, we'll put the route with the lowest +// total fees first. +// +// NOTE: This is part of the sort.Interface implementation. +func (s sortableRoutes) Less(i, j int) bool { + if s[i].TotalFees == s[j].TotalFees { + return s[i].TotalTimeLock < s[j].TotalTimeLock + } + + return s[i].TotalFees < s[j].TotalFees +} + +// Swap swaps the elements with indexes i and j. +// +// NOTE: This is part of the sort.Interface implementation. +func (s sortableRoutes) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + // newRoute returns a fully valid route between the source and target that's // capable of supporting a payment of `amtToSend` after fees are fully // computed. If the route is too long, or the selected path cannot support the diff --git a/routing/router.go b/routing/router.go index e9670928..9b6573eb 100644 --- a/routing/router.go +++ b/routing/router.go @@ -1133,10 +1133,10 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, amt btcutil.Amount) // each path. During this process, some paths may be discarded if they // aren't able to support the total satoshis flow once fees have been // factored in. - validRoutes := make([]*Route, 0, len(shortestPaths)) + validRoutes := make(sortableRoutes, 0, len(shortestPaths)) for _, path := range shortestPaths { // Attempt to make the path into a route. We snip off the first - // hop inthe path as it contains a "self-hop" that is inserted + // hop in the path as it contains a "self-hop" that is inserted // by our KSP algorithm. route, err := newRoute(amt, path[1:]) if err != nil { @@ -1157,13 +1157,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, amt btcutil.Amount) // Finally, we'll sort the set of validate routes to optimize for // lowest total fees, using the required time-lock within the route as // a tie-breaker. - sort.Slice(validRoutes, func(i, j int) bool { - if validRoutes[i].TotalFees == validRoutes[j].TotalFees { - return validRoutes[i].TotalTimeLock < validRoutes[j].TotalTimeLock - } - - return validRoutes[i].TotalFees < validRoutes[j].TotalFees - }) + sort.Sort(validRoutes) log.Debugf("Obtained %v paths sending %v to %x: %v", len(validRoutes), amt, dest, newLogClosure(func() string {