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

     1  package routing
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/davecgh/go-spew/spew"
     7  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     8  	"github.com/decred/dcrlnd/build"
     9  	"github.com/decred/dcrlnd/channeldb"
    10  	"github.com/decred/dcrlnd/lnwire"
    11  	"github.com/decred/dcrlnd/routing/route"
    12  	"github.com/decred/slog"
    13  )
    14  
    15  // BlockPadding is used to increment the finalCltvDelta value for the last hop
    16  // to prevent an HTLC being failed if some blocks are mined while it's in-flight.
    17  const BlockPadding uint16 = 3
    18  
    19  // ValidateCLTVLimit is a helper function that validates that the cltv limit is
    20  // greater than the final cltv delta parameter, optionally including the
    21  // BlockPadding in this calculation.
    22  func ValidateCLTVLimit(limit uint32, delta uint16, includePad bool) error {
    23  	if includePad {
    24  		delta += BlockPadding
    25  	}
    26  
    27  	if limit <= uint32(delta) {
    28  		return fmt.Errorf("cltv limit %v should be greater than %v",
    29  			limit, delta)
    30  	}
    31  
    32  	return nil
    33  }
    34  
    35  // noRouteError encodes a non-critical error encountered during path finding.
    36  type noRouteError uint8
    37  
    38  const (
    39  	// errNoTlvPayload is returned when the destination hop does not support
    40  	// a tlv payload.
    41  	errNoTlvPayload noRouteError = iota
    42  
    43  	// errNoPaymentAddr is returned when the destination hop does not
    44  	// support payment addresses.
    45  	errNoPaymentAddr
    46  
    47  	// errNoPathFound is returned when a path to the target destination does
    48  	// not exist in the graph.
    49  	errNoPathFound
    50  
    51  	// errInsufficientLocalBalance is returned when none of the local
    52  	// channels have enough balance for the payment.
    53  	errInsufficientBalance
    54  
    55  	// errEmptyPaySession is returned when the empty payment session is
    56  	// queried for a route.
    57  	errEmptyPaySession
    58  
    59  	// errUnknownRequiredFeature is returned when the destination node
    60  	// requires an unknown feature.
    61  	errUnknownRequiredFeature
    62  
    63  	// errMissingDependentFeature is returned when the destination node
    64  	// misses a feature that a feature that we require depends on.
    65  	errMissingDependentFeature
    66  )
    67  
    68  var (
    69  	// DefaultShardMinAmt is the default amount beyond which we won't try to
    70  	// further split the payment if no route is found. It is the minimum
    71  	// amount that we use as the shard size when splitting.
    72  	DefaultShardMinAmt = lnwire.NewMAtomsFromAtoms(10000)
    73  )
    74  
    75  // Error returns the string representation of the noRouteError
    76  func (e noRouteError) Error() string {
    77  	switch e {
    78  	case errNoTlvPayload:
    79  		return "destination hop doesn't understand new TLV payloads"
    80  
    81  	case errNoPaymentAddr:
    82  		return "destination hop doesn't understand payment addresses"
    83  
    84  	case errNoPathFound:
    85  		return "unable to find a path to destination"
    86  
    87  	case errEmptyPaySession:
    88  		return "empty payment session"
    89  
    90  	case errInsufficientBalance:
    91  		return "insufficient local balance"
    92  
    93  	case errUnknownRequiredFeature:
    94  		return "unknown required feature"
    95  
    96  	case errMissingDependentFeature:
    97  		return "missing dependent feature"
    98  
    99  	default:
   100  		return "unknown no-route error"
   101  	}
   102  }
   103  
   104  // FailureReason converts a path finding error into a payment-level failure.
   105  func (e noRouteError) FailureReason() channeldb.FailureReason {
   106  	switch e {
   107  	case
   108  		errNoTlvPayload,
   109  		errNoPaymentAddr,
   110  		errNoPathFound,
   111  		errEmptyPaySession,
   112  		errUnknownRequiredFeature,
   113  		errMissingDependentFeature:
   114  
   115  		return channeldb.FailureReasonNoRoute
   116  
   117  	case errInsufficientBalance:
   118  		return channeldb.FailureReasonInsufficientBalance
   119  
   120  	default:
   121  		return channeldb.FailureReasonError
   122  	}
   123  }
   124  
   125  // PaymentSession is used during SendPayment attempts to provide routes to
   126  // attempt. It also defines methods to give the PaymentSession additional
   127  // information learned during the previous attempts.
   128  type PaymentSession interface {
   129  	// RequestRoute returns the next route to attempt for routing the
   130  	// specified HTLC payment to the target node. The returned route should
   131  	// carry at most maxAmt to the target node, and pay at most feeLimit in
   132  	// fees. It can carry less if the payment is MPP. The activeShards
   133  	// argument should be set to instruct the payment session about the
   134  	// number of in flight HTLCS for the payment, such that it can choose
   135  	// splitting strategy accordingly.
   136  	//
   137  	// A noRouteError is returned if a non-critical error is encountered
   138  	// during path finding.
   139  	RequestRoute(maxAmt, feeLimit lnwire.MilliAtom,
   140  		activeShards, height uint32) (*route.Route, error)
   141  
   142  	// UpdateAdditionalEdge takes an additional channel edge policy
   143  	// (private channels) and applies the update from the message. Returns
   144  	// a boolean to indicate whether the update has been applied without
   145  	// error.
   146  	UpdateAdditionalEdge(msg *lnwire.ChannelUpdate, pubKey *secp256k1.PublicKey,
   147  		policy *channeldb.CachedEdgePolicy) bool
   148  
   149  	// GetAdditionalEdgePolicy uses the public key and channel ID to query
   150  	// the ephemeral channel edge policy for additional edges. Returns a nil
   151  	// if nothing found.
   152  	GetAdditionalEdgePolicy(pubKey *secp256k1.PublicKey,
   153  		channelID uint64) *channeldb.CachedEdgePolicy
   154  }
   155  
   156  // paymentSession is used during an HTLC routings session to prune the local
   157  // chain view in response to failures, and also report those failures back to
   158  // MissionControl. The snapshot copied for this session will only ever grow,
   159  // and will now be pruned after a decay like the main view within mission
   160  // control. We do this as we want to avoid the case where we continually try a
   161  // bad edge or route multiple times in a session. This can lead to an infinite
   162  // loop if payment attempts take long enough. An additional set of edges can
   163  // also be provided to assist in reaching the payment's destination.
   164  type paymentSession struct {
   165  	additionalEdges map[route.Vertex][]*channeldb.CachedEdgePolicy
   166  
   167  	getBandwidthHints func(routingGraph) (bandwidthHints, error)
   168  
   169  	payment *LightningPayment
   170  
   171  	empty bool
   172  
   173  	pathFinder pathFinder
   174  
   175  	getRoutingGraph func() (routingGraph, func(), error)
   176  
   177  	// pathFindingConfig defines global parameters that control the
   178  	// trade-off in path finding between fees and probabiity.
   179  	pathFindingConfig PathFindingConfig
   180  
   181  	missionControl MissionController
   182  
   183  	// minShardAmt is the amount beyond which we won't try to further split
   184  	// the payment if no route is found. If the maximum number of htlcs
   185  	// specified in the payment is one, under no circumstances splitting
   186  	// will happen and this value remains unused.
   187  	minShardAmt lnwire.MilliAtom
   188  
   189  	// log is a payment session-specific logger.
   190  	log slog.Logger
   191  }
   192  
   193  // newPaymentSession instantiates a new payment session.
   194  func newPaymentSession(p *LightningPayment,
   195  	getBandwidthHints func(routingGraph) (bandwidthHints, error),
   196  	getRoutingGraph func() (routingGraph, func(), error),
   197  	missionControl MissionController, pathFindingConfig PathFindingConfig) (
   198  	*paymentSession, error) {
   199  
   200  	edges, err := RouteHintsToEdges(p.RouteHints, p.Target)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  
   205  	logPrefix := fmt.Sprintf("PaymentSession(%x):", p.Identifier())
   206  
   207  	return &paymentSession{
   208  		additionalEdges:   edges,
   209  		getBandwidthHints: getBandwidthHints,
   210  		payment:           p,
   211  		pathFinder:        findPath,
   212  		getRoutingGraph:   getRoutingGraph,
   213  		pathFindingConfig: pathFindingConfig,
   214  		missionControl:    missionControl,
   215  		minShardAmt:       DefaultShardMinAmt,
   216  		log:               build.NewPrefixLog(logPrefix, log),
   217  	}, nil
   218  }
   219  
   220  // RequestRoute returns a route which is likely to be capable for successfully
   221  // routing the specified HTLC payment to the target node. Initially the first
   222  // set of paths returned from this method may encounter routing failure along
   223  // the way, however as more payments are sent, mission control will start to
   224  // build an up to date view of the network itself. With each payment a new area
   225  // will be explored, which feeds into the recommendations made for routing.
   226  //
   227  // NOTE: This function is safe for concurrent access.
   228  // NOTE: Part of the PaymentSession interface.
   229  func (p *paymentSession) RequestRoute(maxAmt, feeLimit lnwire.MilliAtom,
   230  	activeShards, height uint32) (*route.Route, error) {
   231  
   232  	if p.empty {
   233  		return nil, errEmptyPaySession
   234  	}
   235  
   236  	// Add BlockPadding to the finalCltvDelta so that the receiving node
   237  	// does not reject the HTLC if some blocks are mined while it's in-flight.
   238  	finalCltvDelta := p.payment.FinalCLTVDelta
   239  	finalCltvDelta += BlockPadding
   240  
   241  	// We need to subtract the final delta before passing it into path
   242  	// finding. The optimal path is independent of the final cltv delta and
   243  	// the path finding algorithm is unaware of this value.
   244  	cltvLimit := p.payment.CltvLimit - uint32(finalCltvDelta)
   245  
   246  	// TODO(roasbeef): sync logic amongst dist sys
   247  
   248  	// Taking into account this prune view, we'll attempt to locate a path
   249  	// to our destination, respecting the recommendations from
   250  	// MissionControl.
   251  	restrictions := &RestrictParams{
   252  		ProbabilitySource:  p.missionControl.GetProbability,
   253  		FeeLimit:           feeLimit,
   254  		OutgoingChannelIDs: p.payment.OutgoingChannelIDs,
   255  		LastHop:            p.payment.LastHop,
   256  		CltvLimit:          cltvLimit,
   257  		DestCustomRecords:  p.payment.DestCustomRecords,
   258  		DestFeatures:       p.payment.DestFeatures,
   259  		PaymentAddr:        p.payment.PaymentAddr,
   260  	}
   261  
   262  	finalHtlcExpiry := int32(height) + int32(finalCltvDelta)
   263  
   264  	// Before we enter the loop below, we'll make sure to respect the max
   265  	// payment shard size (if it's set), which is effectively our
   266  	// client-side MTU that we'll attempt to respect at all times.
   267  	maxShardActive := p.payment.MaxShardAmt != nil
   268  	if maxShardActive && maxAmt > *p.payment.MaxShardAmt {
   269  		p.log.Debug("Clamping payment attempt from %v to %v due to "+
   270  			"max shard size of %v", maxAmt,
   271  			*p.payment.MaxShardAmt, maxAmt)
   272  
   273  		maxAmt = *p.payment.MaxShardAmt
   274  	}
   275  
   276  	for {
   277  		// Get a routing graph.
   278  		routingGraph, cleanup, err := p.getRoutingGraph()
   279  		if err != nil {
   280  			return nil, err
   281  		}
   282  
   283  		// We'll also obtain a set of bandwidthHints from the lower
   284  		// layer for each of our outbound channels. This will allow the
   285  		// path finding to skip any links that aren't active or just
   286  		// don't have enough bandwidth to carry the payment. New
   287  		// bandwidth hints are queried for every new path finding
   288  		// attempt, because concurrent payments may change balances.
   289  		bandwidthHints, err := p.getBandwidthHints(routingGraph)
   290  		if err != nil {
   291  			return nil, err
   292  		}
   293  
   294  		p.log.Debugf("pathfinding for amt=%v", maxAmt)
   295  
   296  		sourceVertex := routingGraph.sourceNode()
   297  
   298  		// Find a route for the current amount.
   299  		path, err := p.pathFinder(
   300  			&graphParams{
   301  				additionalEdges: p.additionalEdges,
   302  				bandwidthHints:  bandwidthHints,
   303  				graph:           routingGraph,
   304  			},
   305  			restrictions, &p.pathFindingConfig,
   306  			sourceVertex, p.payment.Target,
   307  			maxAmt, finalHtlcExpiry,
   308  		)
   309  
   310  		// Close routing graph.
   311  		cleanup()
   312  
   313  		switch {
   314  		case err == errNoPathFound:
   315  			// Don't split if this is a legacy payment without mpp
   316  			// record.
   317  			if p.payment.PaymentAddr == nil {
   318  				p.log.Debugf("not splitting because payment " +
   319  					"address is unspecified")
   320  
   321  				return nil, errNoPathFound
   322  			}
   323  
   324  			if p.payment.DestFeatures == nil {
   325  				p.log.Debug("Not splitting because " +
   326  					"destination DestFeatures is nil")
   327  				return nil, errNoPathFound
   328  			}
   329  
   330  			destFeatures := p.payment.DestFeatures
   331  			if !destFeatures.HasFeature(lnwire.MPPOptional) &&
   332  				!destFeatures.HasFeature(lnwire.AMPOptional) {
   333  
   334  				p.log.Debug("not splitting because " +
   335  					"destination doesn't declare MPP or AMP")
   336  
   337  				return nil, errNoPathFound
   338  			}
   339  
   340  			// No splitting if this is the last shard.
   341  			isLastShard := activeShards+1 >= p.payment.MaxParts
   342  			if isLastShard {
   343  				p.log.Debugf("not splitting because shard "+
   344  					"limit %v has been reached",
   345  					p.payment.MaxParts)
   346  
   347  				return nil, errNoPathFound
   348  			}
   349  
   350  			// This is where the magic happens. If we can't find a
   351  			// route, try it for half the amount.
   352  			maxAmt /= 2
   353  
   354  			// Put a lower bound on the minimum shard size.
   355  			if maxAmt < p.minShardAmt {
   356  				p.log.Debugf("not splitting because minimum "+
   357  					"shard amount %v has been reached",
   358  					p.minShardAmt)
   359  
   360  				return nil, errNoPathFound
   361  			}
   362  
   363  			// Go pathfinding.
   364  			continue
   365  
   366  		// If there isn't enough local bandwidth, there is no point in
   367  		// splitting. It won't be possible to create a complete set in
   368  		// any case, but the sent out partial payments would be held by
   369  		// the receiver until the mpp timeout.
   370  		case err == errInsufficientBalance:
   371  			p.log.Debug("not splitting because local balance " +
   372  				"is insufficient")
   373  
   374  			return nil, err
   375  
   376  		case err != nil:
   377  			return nil, err
   378  		}
   379  
   380  		// With the next candidate path found, we'll attempt to turn
   381  		// this into a route by applying the time-lock and fee
   382  		// requirements.
   383  		route, err := newRoute(
   384  			sourceVertex, path, height,
   385  			finalHopParams{
   386  				amt:         maxAmt,
   387  				totalAmt:    p.payment.Amount,
   388  				cltvDelta:   finalCltvDelta,
   389  				records:     p.payment.DestCustomRecords,
   390  				paymentAddr: p.payment.PaymentAddr,
   391  			},
   392  		)
   393  		if err != nil {
   394  			return nil, err
   395  		}
   396  
   397  		return route, err
   398  	}
   399  }
   400  
   401  // UpdateAdditionalEdge updates the channel edge policy for a private edge. It
   402  // validates the message signature and checks it's up to date, then applies the
   403  // updates to the supplied policy. It returns a boolean to indicate whether
   404  // there's an error when applying the updates.
   405  func (p *paymentSession) UpdateAdditionalEdge(msg *lnwire.ChannelUpdate,
   406  	pubKey *secp256k1.PublicKey, policy *channeldb.CachedEdgePolicy) bool {
   407  
   408  	// Validate the message signature.
   409  	if err := VerifyChannelUpdateSignature(msg, pubKey); err != nil {
   410  		log.Errorf(
   411  			"Unable to validate channel update signature: %v", err,
   412  		)
   413  		return false
   414  	}
   415  
   416  	// Update channel policy for the additional edge.
   417  	policy.TimeLockDelta = msg.TimeLockDelta
   418  	policy.FeeBaseMAtoms = lnwire.MilliAtom(msg.BaseFee)
   419  	policy.FeeProportionalMillionths = lnwire.MilliAtom(msg.FeeRate)
   420  
   421  	log.Debugf("New private channel update applied: %v",
   422  		newLogClosure(func() string { return spew.Sdump(msg) }))
   423  
   424  	return true
   425  }
   426  
   427  // GetAdditionalEdgePolicy uses the public key and channel ID to query the
   428  // ephemeral channel edge policy for additional edges. Returns a nil if nothing
   429  // found.
   430  func (p *paymentSession) GetAdditionalEdgePolicy(pubKey *secp256k1.PublicKey,
   431  	channelID uint64) *channeldb.CachedEdgePolicy {
   432  
   433  	target := route.NewVertex(pubKey)
   434  
   435  	edges, ok := p.additionalEdges[target]
   436  	if !ok {
   437  		return nil
   438  	}
   439  
   440  	for _, edge := range edges {
   441  		if edge.ChannelID != channelID {
   442  			continue
   443  		}
   444  
   445  		return edge
   446  	}
   447  
   448  	return nil
   449  }