2019-01-24 20:50:53 +01:00
|
|
|
package routing
|
|
|
|
|
|
|
|
import (
|
2019-11-21 12:05:43 +01:00
|
|
|
"errors"
|
2019-01-24 20:50:53 +01:00
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/channeldb"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2019-04-05 17:36:11 +02:00
|
|
|
"github.com/lightningnetwork/lnd/routing/route"
|
2019-01-24 20:50:53 +01:00
|
|
|
)
|
|
|
|
|
2019-07-24 02:59:31 +02:00
|
|
|
// BlockPadding is used to increment the finalCltvDelta value for the last hop
|
|
|
|
// to prevent an HTLC being failed if some blocks are mined while it's in-flight.
|
|
|
|
const BlockPadding uint16 = 3
|
|
|
|
|
2019-11-21 12:05:43 +01:00
|
|
|
var (
|
2020-04-01 00:13:25 +02:00
|
|
|
// errEmptyPaySession is returned when the empty payment session is
|
|
|
|
// queried for a route.
|
|
|
|
errEmptyPaySession = errors.New("empty payment session")
|
2019-11-21 12:05:43 +01:00
|
|
|
)
|
|
|
|
|
2019-05-23 20:05:30 +02:00
|
|
|
// PaymentSession is used during SendPayment attempts to provide routes to
|
|
|
|
// attempt. It also defines methods to give the PaymentSession additional
|
|
|
|
// information learned during the previous attempts.
|
|
|
|
type PaymentSession interface {
|
|
|
|
// RequestRoute returns the next route to attempt for routing the
|
2020-04-01 00:13:22 +02:00
|
|
|
// specified HTLC payment to the target node. The returned route should
|
|
|
|
// carry at most maxAmt to the target node, and pay at most feeLimit in
|
|
|
|
// fees. It can carry less if the payment is MPP. The activeShards
|
|
|
|
// argument should be set to instruct the payment session about the
|
|
|
|
// number of in flight HTLCS for the payment, such that it can choose
|
|
|
|
// splitting strategy accordingly.
|
|
|
|
RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|
|
|
activeShards, height uint32) (*route.Route, error)
|
2019-05-23 20:05:30 +02:00
|
|
|
}
|
|
|
|
|
2019-01-24 20:50:53 +01:00
|
|
|
// paymentSession is used during an HTLC routings session to prune the local
|
|
|
|
// chain view in response to failures, and also report those failures back to
|
2019-05-23 20:05:30 +02:00
|
|
|
// MissionControl. The snapshot copied for this session will only ever grow,
|
2019-01-24 20:50:53 +01:00
|
|
|
// and will now be pruned after a decay like the main view within mission
|
|
|
|
// control. We do this as we want to avoid the case where we continually try a
|
|
|
|
// bad edge or route multiple times in a session. This can lead to an infinite
|
|
|
|
// loop if payment attempts take long enough. An additional set of edges can
|
|
|
|
// also be provided to assist in reaching the payment's destination.
|
|
|
|
type paymentSession struct {
|
2019-04-05 17:36:11 +02:00
|
|
|
additionalEdges map[route.Vertex][]*channeldb.ChannelEdgePolicy
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2019-08-17 09:58:36 +02:00
|
|
|
getBandwidthHints func() (map[uint64]lnwire.MilliSatoshi, error)
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2019-06-18 18:30:56 +02:00
|
|
|
sessionSource *SessionSource
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2020-04-01 00:13:22 +02:00
|
|
|
payment *LightningPayment
|
|
|
|
|
2020-04-01 00:13:25 +02:00
|
|
|
empty bool
|
2019-02-13 11:35:55 +01:00
|
|
|
|
|
|
|
pathFinder pathFinder
|
2019-01-24 20:50:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// RequestRoute returns a route which is likely to be capable for successfully
|
|
|
|
// routing the specified HTLC payment to the target node. Initially the first
|
|
|
|
// set of paths returned from this method may encounter routing failure along
|
|
|
|
// the way, however as more payments are sent, mission control will start to
|
|
|
|
// build an up to date view of the network itself. With each payment a new area
|
|
|
|
// will be explored, which feeds into the recommendations made for routing.
|
|
|
|
//
|
|
|
|
// NOTE: This function is safe for concurrent access.
|
2019-05-23 20:05:30 +02:00
|
|
|
// NOTE: Part of the PaymentSession interface.
|
2020-04-01 00:13:22 +02:00
|
|
|
func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliSatoshi,
|
|
|
|
activeShards, height uint32) (*route.Route, error) {
|
2019-01-24 20:50:53 +01:00
|
|
|
|
2020-04-01 00:13:25 +02:00
|
|
|
if p.empty {
|
|
|
|
return nil, errEmptyPaySession
|
2019-01-24 20:50:53 +01:00
|
|
|
}
|
|
|
|
|
2019-07-24 02:59:31 +02:00
|
|
|
// Add BlockPadding to the finalCltvDelta so that the receiving node
|
|
|
|
// does not reject the HTLC if some blocks are mined while it's in-flight.
|
2020-04-01 00:13:22 +02:00
|
|
|
finalCltvDelta := p.payment.FinalCLTVDelta
|
2019-07-24 02:59:31 +02:00
|
|
|
finalCltvDelta += BlockPadding
|
|
|
|
|
2019-10-11 21:46:10 +02:00
|
|
|
// We need to subtract the final delta before passing it into path
|
|
|
|
// finding. The optimal path is independent of the final cltv delta and
|
|
|
|
// the path finding algorithm is unaware of this value.
|
2020-04-01 00:13:22 +02:00
|
|
|
cltvLimit := p.payment.CltvLimit - uint32(finalCltvDelta)
|
2019-02-13 11:53:32 +01:00
|
|
|
|
2019-01-24 20:50:53 +01:00
|
|
|
// TODO(roasbeef): sync logic amongst dist sys
|
|
|
|
|
|
|
|
// Taking into account this prune view, we'll attempt to locate a path
|
|
|
|
// to our destination, respecting the recommendations from
|
2019-05-23 20:05:30 +02:00
|
|
|
// MissionControl.
|
2019-06-18 18:30:56 +02:00
|
|
|
ss := p.sessionSource
|
|
|
|
|
|
|
|
restrictions := &RestrictParams{
|
2019-07-29 15:10:58 +02:00
|
|
|
ProbabilitySource: ss.MissionControl.GetProbability,
|
2020-04-01 00:13:22 +02:00
|
|
|
FeeLimit: feeLimit,
|
2020-04-01 00:13:22 +02:00
|
|
|
OutgoingChannelID: p.payment.OutgoingChannelID,
|
|
|
|
LastHop: p.payment.LastHop,
|
2019-06-20 12:03:45 +02:00
|
|
|
CltvLimit: cltvLimit,
|
2020-04-01 00:13:22 +02:00
|
|
|
DestCustomRecords: p.payment.DestCustomRecords,
|
|
|
|
DestFeatures: p.payment.DestFeatures,
|
|
|
|
PaymentAddr: p.payment.PaymentAddr,
|
2019-06-18 18:30:56 +02:00
|
|
|
}
|
|
|
|
|
2019-08-17 09:58:36 +02:00
|
|
|
// We'll also obtain a set of bandwidthHints from the lower layer for
|
|
|
|
// each of our outbound channels. This will allow the path finding to
|
|
|
|
// skip any links that aren't active or just don't have enough bandwidth
|
|
|
|
// to carry the payment. New bandwidth hints are queried for every new
|
|
|
|
// path finding attempt, because concurrent payments may change
|
|
|
|
// balances.
|
|
|
|
bandwidthHints, err := p.getBandwidthHints()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-12-17 11:55:03 +01:00
|
|
|
finalHtlcExpiry := int32(height) + int32(finalCltvDelta)
|
|
|
|
|
2019-02-13 11:35:55 +01:00
|
|
|
path, err := p.pathFinder(
|
2019-01-24 20:50:53 +01:00
|
|
|
&graphParams{
|
2019-06-18 18:30:56 +02:00
|
|
|
graph: ss.Graph,
|
2019-01-24 20:50:53 +01:00
|
|
|
additionalEdges: p.additionalEdges,
|
2019-08-17 09:58:36 +02:00
|
|
|
bandwidthHints: bandwidthHints,
|
2019-01-24 20:50:53 +01:00
|
|
|
},
|
2019-06-20 12:03:45 +02:00
|
|
|
restrictions, &ss.PathFindingConfig,
|
2020-04-01 00:13:22 +02:00
|
|
|
ss.SelfNode.PubKeyBytes, p.payment.Target,
|
2020-04-01 00:13:22 +02:00
|
|
|
maxAmt, finalHtlcExpiry,
|
2019-01-24 20:50:53 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// With the next candidate path found, we'll attempt to turn this into
|
|
|
|
// a route by applying the time-lock and fee requirements.
|
2019-06-18 18:30:56 +02:00
|
|
|
sourceVertex := route.Vertex(ss.SelfNode.PubKeyBytes)
|
2019-01-24 20:50:53 +01:00
|
|
|
route, err := newRoute(
|
2019-12-19 08:55:08 +01:00
|
|
|
sourceVertex, path, height,
|
|
|
|
finalHopParams{
|
2020-04-01 00:13:22 +02:00
|
|
|
amt: maxAmt,
|
2019-12-19 08:56:59 +01:00
|
|
|
cltvDelta: finalCltvDelta,
|
2020-04-01 00:13:22 +02:00
|
|
|
records: p.payment.DestCustomRecords,
|
|
|
|
paymentAddr: p.payment.PaymentAddr,
|
2019-12-19 08:55:08 +01:00
|
|
|
},
|
2019-01-24 20:50:53 +01:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
// TODO(roasbeef): return which edge/vertex didn't work
|
|
|
|
// out
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return route, err
|
|
|
|
}
|