github.com/decred/dcrlnd@v0.7.6/routing/result_interpretation.go (about) 1 package routing 2 3 import ( 4 "fmt" 5 6 "github.com/decred/dcrlnd/channeldb" 7 "github.com/decred/dcrlnd/lnwire" 8 "github.com/decred/dcrlnd/routing/route" 9 ) 10 11 // Instantiate variables to allow taking a reference from the failure reason. 12 var ( 13 reasonError = channeldb.FailureReasonError 14 reasonIncorrectDetails = channeldb.FailureReasonPaymentDetails 15 ) 16 17 // pairResult contains the result of the interpretation of a payment attempt for 18 // a specific node pair. 19 type pairResult struct { 20 // amt is the amount that was forwarded for this pair. Can be set to 21 // zero for failures that are amount independent. 22 amt lnwire.MilliAtom 23 24 // success indicates whether the payment attempt was successful through 25 // this pair. 26 success bool 27 } 28 29 // failPairResult creates a new result struct for a failure. 30 func failPairResult(minPenalizeAmt lnwire.MilliAtom) pairResult { 31 return pairResult{ 32 amt: minPenalizeAmt, 33 } 34 } 35 36 // newSuccessPairResult creates a new result struct for a success. 37 func successPairResult(successAmt lnwire.MilliAtom) pairResult { 38 return pairResult{ 39 success: true, 40 amt: successAmt, 41 } 42 } 43 44 // String returns the human-readable representation of a pair result. 45 func (p pairResult) String() string { 46 var resultType string 47 if p.success { 48 resultType = "success" 49 } else { 50 resultType = "failed" 51 } 52 53 return fmt.Sprintf("%v (amt=%v)", resultType, p.amt) 54 } 55 56 // interpretedResult contains the result of the interpretation of a payment 57 // attempt. 58 type interpretedResult struct { 59 // nodeFailure points to a node pubkey if all channels of that node are 60 // responsible for the result. 61 nodeFailure *route.Vertex 62 63 // pairResults contains a map of node pairs for which we have a result. 64 pairResults map[DirectedNodePair]pairResult 65 66 // finalFailureReason is set to a non-nil value if it makes no more 67 // sense to start another payment attempt. It will contain the reason 68 // why. 69 finalFailureReason *channeldb.FailureReason 70 71 // policyFailure is set to a node pair if there is a policy failure on 72 // that connection. This is used to control the second chance logic for 73 // policy failures. 74 policyFailure *DirectedNodePair 75 } 76 77 // interpretResult interprets a payment outcome and returns an object that 78 // contains information required to update mission control. 79 func interpretResult(rt *route.Route, success bool, failureSrcIdx *int, 80 failure lnwire.FailureMessage) *interpretedResult { 81 82 i := &interpretedResult{ 83 pairResults: make(map[DirectedNodePair]pairResult), 84 } 85 86 if success { 87 i.processSuccess(rt) 88 } else { 89 i.processFail(rt, failureSrcIdx, failure) 90 } 91 return i 92 } 93 94 // processSuccess processes a successful payment attempt. 95 func (i *interpretedResult) processSuccess(route *route.Route) { 96 // For successes, all nodes must have acted in the right way. Therefore 97 // we mark all of them with a success result. 98 i.successPairRange(route, 0, len(route.Hops)-1) 99 } 100 101 // processFail processes a failed payment attempt. 102 func (i *interpretedResult) processFail( 103 rt *route.Route, errSourceIdx *int, 104 failure lnwire.FailureMessage) { 105 106 if errSourceIdx == nil { 107 i.processPaymentOutcomeUnknown(rt) 108 return 109 } 110 111 switch *errSourceIdx { 112 113 // We are the source of the failure. 114 case 0: 115 i.processPaymentOutcomeSelf(rt, failure) 116 117 // A failure from the final hop was received. 118 case len(rt.Hops): 119 i.processPaymentOutcomeFinal( 120 rt, failure, 121 ) 122 123 // An intermediate hop failed. Interpret the outcome, update reputation 124 // and try again. 125 default: 126 i.processPaymentOutcomeIntermediate( 127 rt, *errSourceIdx, failure, 128 ) 129 } 130 } 131 132 // processPaymentOutcomeSelf handles failures sent by ourselves. 133 func (i *interpretedResult) processPaymentOutcomeSelf( 134 rt *route.Route, failure lnwire.FailureMessage) { 135 136 switch failure.(type) { 137 138 // We receive a malformed htlc failure from our peer. We trust ourselves 139 // to send the correct htlc, so our peer must be at fault. 140 case *lnwire.FailInvalidOnionVersion, 141 *lnwire.FailInvalidOnionHmac, 142 *lnwire.FailInvalidOnionKey: 143 144 i.failNode(rt, 1) 145 146 // If this was a payment to a direct peer, we can stop trying. 147 if len(rt.Hops) == 1 { 148 i.finalFailureReason = &reasonError 149 } 150 151 // Any other failure originating from ourselves should be temporary and 152 // caused by changing conditions between path finding and execution of 153 // the payment. We just retry and trust that the information locally 154 // available in the link has been updated. 155 default: 156 log.Warnf("Routing failure for local channel %v occurred", 157 rt.Hops[0].ChannelID) 158 } 159 } 160 161 // processPaymentOutcomeFinal handles failures sent by the final hop. 162 func (i *interpretedResult) processPaymentOutcomeFinal( 163 route *route.Route, failure lnwire.FailureMessage) { 164 165 n := len(route.Hops) 166 167 // If a failure from the final node is received, we will fail the 168 // payment in almost all cases. Only when the penultimate node sends an 169 // incorrect htlc, we want to retry via another route. Invalid onion 170 // failures are not expected, because the final node wouldn't be able to 171 // encrypt that failure. 172 switch failure.(type) { 173 174 // Expiry or amount of the HTLC doesn't match the onion, try another 175 // route. 176 case *lnwire.FailFinalIncorrectCltvExpiry, 177 *lnwire.FailFinalIncorrectHtlcAmount: 178 179 // We trust ourselves. If this is a direct payment, we penalize 180 // the final node and fail the payment. 181 if n == 1 { 182 i.failNode(route, n) 183 i.finalFailureReason = &reasonError 184 185 return 186 } 187 188 // Otherwise penalize the last pair of the route and retry. 189 // Either the final node is at fault, or it gets sent a bad htlc 190 // from its predecessor. 191 i.failPair(route, n-1) 192 193 // The other hops relayed corectly, so assign those pairs a 194 // success result. At this point, n >= 2. 195 i.successPairRange(route, 0, n-2) 196 197 // We are using wrong payment hash or amount, fail the payment. 198 case *lnwire.FailIncorrectPaymentAmount, 199 *lnwire.FailIncorrectDetails: 200 201 // Assign all pairs a success result, as the payment reached the 202 // destination correctly. 203 i.successPairRange(route, 0, n-1) 204 205 i.finalFailureReason = &reasonIncorrectDetails 206 207 // The HTLC that was extended to the final hop expires too soon. Fail 208 // the payment, because we may be using the wrong final cltv delta. 209 case *lnwire.FailFinalExpiryTooSoon: 210 // TODO(roasbeef): can happen to to race condition, try again 211 // with recent block height 212 213 // TODO(joostjager): can also happen because a node delayed 214 // deliberately. What to penalize? 215 i.finalFailureReason = &reasonIncorrectDetails 216 217 case *lnwire.FailMPPTimeout: 218 // Assign all pairs a success result, as the payment reached the 219 // destination correctly. Continue the payment process. 220 i.successPairRange(route, 0, n-1) 221 222 default: 223 // All other errors are considered terminal if coming from the 224 // final hop. They indicate that something is wrong at the 225 // recipient, so we do apply a penalty. 226 i.failNode(route, n) 227 228 // Other channels in the route forwarded correctly. 229 if n >= 2 { 230 i.successPairRange(route, 0, n-2) 231 } 232 233 i.finalFailureReason = &reasonError 234 } 235 } 236 237 // processPaymentOutcomeIntermediate handles failures sent by an intermediate 238 // hop. 239 func (i *interpretedResult) processPaymentOutcomeIntermediate( 240 route *route.Route, errorSourceIdx int, 241 failure lnwire.FailureMessage) { 242 243 reportOutgoing := func() { 244 i.failPair( 245 route, errorSourceIdx, 246 ) 247 } 248 249 reportOutgoingBalance := func() { 250 i.failPairBalance( 251 route, errorSourceIdx, 252 ) 253 254 // All nodes up to the failing pair must have forwarded 255 // successfully. 256 i.successPairRange(route, 0, errorSourceIdx-1) 257 } 258 259 reportIncoming := func() { 260 // We trust ourselves. If the error comes from the first hop, we 261 // can penalize the whole node. In that case there is no 262 // uncertainty as to which node to blame. 263 if errorSourceIdx == 1 { 264 i.failNode(route, errorSourceIdx) 265 return 266 } 267 268 // Otherwise report the incoming pair. 269 i.failPair( 270 route, errorSourceIdx-1, 271 ) 272 273 // All nodes up to the failing pair must have forwarded 274 // successfully. 275 if errorSourceIdx > 1 { 276 i.successPairRange(route, 0, errorSourceIdx-2) 277 } 278 } 279 280 reportNode := func() { 281 // Fail only the node that reported the failure. 282 i.failNode(route, errorSourceIdx) 283 284 // Other preceding channels in the route forwarded correctly. 285 if errorSourceIdx > 1 { 286 i.successPairRange(route, 0, errorSourceIdx-2) 287 } 288 } 289 290 reportAll := func() { 291 // We trust ourselves. If the error comes from the first hop, we 292 // can penalize the whole node. In that case there is no 293 // uncertainty as to which node to blame. 294 if errorSourceIdx == 1 { 295 i.failNode(route, errorSourceIdx) 296 return 297 } 298 299 // Otherwise penalize all pairs up to the error source. This 300 // includes our own outgoing connection. 301 i.failPairRange( 302 route, 0, errorSourceIdx-1, 303 ) 304 } 305 306 switch failure.(type) { 307 308 // If a node reports onion payload corruption or an invalid version, 309 // that node may be responsible, but it could also be that it is just 310 // relaying a malformed htlc failure from it successor. By reporting the 311 // outgoing channel set, we will surely hit the responsible node. At 312 // this point, it is not possible that the node's predecessor corrupted 313 // the onion blob. If the predecessor would have corrupted the payload, 314 // the error source wouldn't have been able to encrypt this failure 315 // message for us. 316 case *lnwire.FailInvalidOnionVersion, 317 *lnwire.FailInvalidOnionHmac, 318 *lnwire.FailInvalidOnionKey: 319 320 reportOutgoing() 321 322 // If InvalidOnionPayload is received, we penalize only the reporting 323 // node. We know the preceding hop didn't corrupt the onion, since the 324 // reporting node is able to send the failure. We assume that we 325 // constructed a valid onion payload and that the failure is most likely 326 // an unknown required type or a bug in their implementation. 327 case *lnwire.InvalidOnionPayload: 328 reportNode() 329 330 // If the next hop in the route wasn't known or offline, we'll only 331 // penalize the channel set which we attempted to route over. This is 332 // conservative, and it can handle faulty channels between nodes 333 // properly. Additionally, this guards against routing nodes returning 334 // errors in order to attempt to black list another node. 335 case *lnwire.FailUnknownNextPeer: 336 reportOutgoing() 337 338 // Some implementations use this error when the next hop is offline, so we 339 // do the same as FailUnknownNextPeer and also process the channel update. 340 case *lnwire.FailChannelDisabled: 341 342 // Set the node pair for which a channel update may be out of 343 // date. The second chance logic uses the policyFailure field. 344 i.policyFailure = &DirectedNodePair{ 345 From: route.Hops[errorSourceIdx-1].PubKeyBytes, 346 To: route.Hops[errorSourceIdx].PubKeyBytes, 347 } 348 349 reportOutgoing() 350 351 // All nodes up to the failing pair must have forwarded 352 // successfully. 353 i.successPairRange(route, 0, errorSourceIdx-1) 354 355 // If we get a permanent channel, we'll prune the channel set in both 356 // directions and continue with the rest of the routes. 357 case *lnwire.FailPermanentChannelFailure: 358 reportOutgoing() 359 360 // When an HTLC parameter is incorrect, the node sending the error may 361 // be doing something wrong. But it could also be that its predecessor 362 // is intentionally modifying the htlc parameters that we instructed it 363 // via the hop payload. Therefore we penalize the incoming node pair. A 364 // third cause of this error may be that we have an out of date channel 365 // update. This is handled by the second chance logic up in mission 366 // control. 367 case *lnwire.FailAmountBelowMinimum, 368 *lnwire.FailFeeInsufficient, 369 *lnwire.FailIncorrectCltvExpiry: 370 371 // Set the node pair for which a channel update may be out of 372 // date. The second chance logic uses the policyFailure field. 373 i.policyFailure = &DirectedNodePair{ 374 From: route.Hops[errorSourceIdx-1].PubKeyBytes, 375 To: route.Hops[errorSourceIdx].PubKeyBytes, 376 } 377 378 // We report incoming channel. If a second pair is granted in 379 // mission control, this report is ignored. 380 reportIncoming() 381 382 // If the outgoing channel doesn't have enough capacity, we penalize. 383 // But we penalize only in a single direction and only for amounts 384 // greater than the attempted amount. 385 case *lnwire.FailTemporaryChannelFailure: 386 reportOutgoingBalance() 387 388 // If FailExpiryTooSoon is received, there must have been some delay 389 // along the path. We can't know which node is causing the delay, so we 390 // penalize all of them up to the error source. 391 // 392 // Alternatively it could also be that we ourselves have fallen behind 393 // somehow. We ignore that case for now. 394 case *lnwire.FailExpiryTooSoon: 395 reportAll() 396 397 // In all other cases, we penalize the reporting node. These are all 398 // failures that should not happen. 399 default: 400 i.failNode(route, errorSourceIdx) 401 } 402 } 403 404 // processPaymentOutcomeUnknown processes a payment outcome for which no failure 405 // message or source is available. 406 func (i *interpretedResult) processPaymentOutcomeUnknown(route *route.Route) { 407 n := len(route.Hops) 408 409 // If this is a direct payment, the destination must be at fault. 410 if n == 1 { 411 i.failNode(route, n) 412 i.finalFailureReason = &reasonError 413 return 414 } 415 416 // Otherwise penalize all channels in the route to make sure the 417 // responsible node is at least hit too. We even penalize the connection 418 // to our own peer, because that peer could also be responsible. 419 i.failPairRange(route, 0, n-1) 420 } 421 422 // failNode marks the node indicated by idx in the route as failed. It also 423 // marks the incoming and outgoing channels of the node as failed. This function 424 // intentionally panics when the self node is failed. 425 func (i *interpretedResult) failNode(rt *route.Route, idx int) { 426 // Mark the node as failing. 427 i.nodeFailure = &rt.Hops[idx-1].PubKeyBytes 428 429 // Mark the incoming connection as failed for the node. We intent to 430 // penalize as much as we can for a node level failure, including future 431 // outgoing traffic for this connection. The pair as it is returned by 432 // getPair is penalized in the original and the reversed direction. Note 433 // that this will also affect the score of the failing node's peers. 434 // This is necessary to prevent future routes from keep going into the 435 // same node again. 436 incomingChannelIdx := idx - 1 437 inPair, _ := getPair(rt, incomingChannelIdx) 438 i.pairResults[inPair] = failPairResult(0) 439 i.pairResults[inPair.Reverse()] = failPairResult(0) 440 441 // If not the ultimate node, mark the outgoing connection as failed for 442 // the node. 443 if idx < len(rt.Hops) { 444 outgoingChannelIdx := idx 445 outPair, _ := getPair(rt, outgoingChannelIdx) 446 i.pairResults[outPair] = failPairResult(0) 447 i.pairResults[outPair.Reverse()] = failPairResult(0) 448 } 449 } 450 451 // failPairRange marks the node pairs from node fromIdx to node toIdx as failed 452 // in both direction. 453 func (i *interpretedResult) failPairRange( 454 rt *route.Route, fromIdx, toIdx int) { 455 456 for idx := fromIdx; idx <= toIdx; idx++ { 457 i.failPair(rt, idx) 458 } 459 } 460 461 // failPair marks a pair as failed in both directions. 462 func (i *interpretedResult) failPair( 463 rt *route.Route, idx int) { 464 465 pair, _ := getPair(rt, idx) 466 467 // Report pair in both directions without a minimum penalization amount. 468 i.pairResults[pair] = failPairResult(0) 469 i.pairResults[pair.Reverse()] = failPairResult(0) 470 } 471 472 // failPairBalance marks a pair as failed with a minimum penalization amount. 473 func (i *interpretedResult) failPairBalance( 474 rt *route.Route, channelIdx int) { 475 476 pair, amt := getPair(rt, channelIdx) 477 478 i.pairResults[pair] = failPairResult(amt) 479 } 480 481 // successPairRange marks the node pairs from node fromIdx to node toIdx as 482 // succeeded. 483 func (i *interpretedResult) successPairRange( 484 rt *route.Route, fromIdx, toIdx int) { 485 486 for idx := fromIdx; idx <= toIdx; idx++ { 487 pair, amt := getPair(rt, idx) 488 489 i.pairResults[pair] = successPairResult(amt) 490 } 491 } 492 493 // getPair returns a node pair from the route and the amount passed between that 494 // pair. 495 func getPair(rt *route.Route, channelIdx int) (DirectedNodePair, 496 lnwire.MilliAtom) { 497 498 nodeTo := rt.Hops[channelIdx].PubKeyBytes 499 var ( 500 nodeFrom route.Vertex 501 amt lnwire.MilliAtom 502 ) 503 504 if channelIdx == 0 { 505 nodeFrom = rt.SourcePubKey 506 amt = rt.TotalAmount 507 } else { 508 nodeFrom = rt.Hops[channelIdx-1].PubKeyBytes 509 amt = rt.Hops[channelIdx-1].AmtToForward 510 } 511 512 pair := NewDirectedNodePair(nodeFrom, nodeTo) 513 514 return pair, amt 515 }