diff --git a/routing/result_interpretation.go b/routing/result_interpretation.go index ad7f7d2fd..4118286e6 100644 --- a/routing/result_interpretation.go +++ b/routing/result_interpretation.go @@ -267,6 +267,21 @@ func (i *interpretedResult) processPaymentOutcomeFinal( // destination correctly. Continue the payment process. i.successPairRange(route, 0, n-1) + // We do not expect to receive an invalid blinding error from the final + // node in the route. This could erroneously happen in the following + // cases: + // 1. Unblinded node: misuses the error code. + // 2. A receiving introduction node: erroneously sends the error code, + // as the spec indicates that receiving introduction nodes should + // use regular errors. + // + // Note that we expect the case where this error is sent from a node + // after the introduction node to be handled elsewhere as this is part + // of a more general class of errors where the introduction node has + // failed to convert errors for the blinded route. + case *lnwire.FailInvalidBlinding: + failNode() + // All other errors are considered terminal if coming from the // final hop. They indicate that something is wrong at the // recipient, so we do apply a penalty. diff --git a/routing/result_interpretation_test.go b/routing/result_interpretation_test.go index 92ac255ff..bf7d6d3ed 100644 --- a/routing/result_interpretation_test.go +++ b/routing/result_interpretation_test.go @@ -106,6 +106,21 @@ var ( {PubKeyBytes: hops[3], AmtToForward: 58}, }, } + + // blindedIntroReceiver is a blinded path where the introduction node + // is the recipient. + blindedIntroReceiver = route.Route{ + SourcePubKey: hops[0], + TotalAmount: 100, + Hops: []*route.Hop{ + {PubKeyBytes: hops[1], AmtToForward: 95}, + { + PubKeyBytes: hops[2], + AmtToForward: 90, + BlindingPoint: blindingPoint, + }, + }, + } ) func getTestPair(from, to int) DirectedNodePair { @@ -574,6 +589,41 @@ var resultTestCases = []resultTestCase{ nodeFailure: &hops[1], }, }, + // A node in a non-blinded route returns a blinding related error. + { + name: "final node unexpected blinding", + route: &routeThreeHop, + failureSrcIdx: 3, + failure: &lnwire.FailInvalidBlinding{}, + + expectedResult: &interpretedResult{ + pairResults: map[DirectedNodePair]pairResult{ + getTestPair(0, 1): successPairResult(100), + getTestPair(1, 2): successPairResult(99), + getTestPair(2, 3): failPairResult(0), + getTestPair(3, 2): failPairResult(0), + }, + nodeFailure: &hops[3], + finalFailureReason: &reasonError, + }, + }, + // Introduction node returns invalid blinding erroneously. + { + name: "final node intro blinding", + route: &blindedIntroReceiver, + failureSrcIdx: 2, + failure: &lnwire.FailInvalidBlinding{}, + + expectedResult: &interpretedResult{ + pairResults: map[DirectedNodePair]pairResult{ + getTestPair(0, 1): successPairResult(100), + getTestPair(1, 2): failPairResult(0), + getTestPair(2, 1): failPairResult(0), + }, + nodeFailure: &hops[2], + finalFailureReason: &reasonError, + }, + }, } // TestResultInterpretation executes a list of test cases that test the result