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 }