Merge pull request #3470 from cfromknecht/invalid-onion-payload

htlcswitch+lnwire: invalid onion payload
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2019-11-01 18:58:00 -07:00
commit acd8a6e302
9 files changed
+457 -66

No files matched your search

+19 -1
View File
@@ -265,7 +265,17 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(
// All nodes up to the failing pair must have forwarded
// successfully.
if errorSourceIdx > 2 {
if errorSourceIdx > 1 {
i.successPairRange(route, 0, errorSourceIdx-2)
}
}
reportNode := func() {
// Fail only the node that reported the failure.
i.failNode(route, errorSourceIdx)
// Other preceding channels in the route forwarded correctly.
if errorSourceIdx > 1 {
i.successPairRange(route, 0, errorSourceIdx-2)
}
}
@@ -302,6 +312,14 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(
reportOutgoing()
// If InvalidOnionPayload is received, we penalize only the reporting
// node. We know the preceding hop didn't corrupt the onion, since the
// reporting node is able to send the failure. We assume that we
// constructed a valid onion payload and that the failure is most likely
// an unknown required type or a bug in their implementation.
case *lnwire.InvalidOnionPayload:
reportNode()
// If the next hop in the route wasn't known or offline, we'll only
// penalize the channel set which we attempted to route over. This is
// conservative, and it can handle faulty channels between nodes
+99 -1
View File
@@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
@@ -47,6 +48,11 @@ func getTestPair(from, to int) DirectedNodePair {
return NewDirectedNodePair(hops[from], hops[to])
}
func getPolicyFailure(from, to int) *DirectedNodePair {
pair := getTestPair(from, to)
return &pair
}
type resultTestCase struct {
name string
route *route.Route
@@ -169,6 +175,97 @@ var resultTestCases = []resultTestCase{
},
},
},
// Tests that a fee insufficient failure to an intermediate hop with
// index 2 results in the first hop marked as success, and then a
// bidirectional failure for the incoming channel. It should also result
// in a policy failure for the outgoing hop.
{
name: "fail fee insufficient intermediate",
route: &routeFourHop,
failureSrcIdx: 2,
failure: lnwire.NewFeeInsufficient(0, lnwire.ChannelUpdate{}),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): {
success: true,
},
getTestPair(1, 2): {},
getTestPair(2, 1): {},
},
policyFailure: getPolicyFailure(2, 3),
},
},
// Tests an invalid onion payload from a final hop. The final hop should
// be failed while the proceeding hops are reproed as successes. The
// failure is terminal since the receiver can't process our onion.
{
name: "fail invalid onion payload final hop",
route: &routeFourHop,
failureSrcIdx: 4,
failure: lnwire.NewInvalidOnionPayload(0, 0),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): {
success: true,
},
getTestPair(1, 2): {
success: true,
},
getTestPair(2, 3): {
success: true,
},
getTestPair(4, 3): {},
},
finalFailureReason: &reasonError,
nodeFailure: &hops[4],
},
},
// Tests an invalid onion payload from an intermediate hop. Only the
// reporting node should be failed. The failure is non-terminal since we
// can still try other paths.
{
name: "fail invalid onion payload intermediate",
route: &routeFourHop,
failureSrcIdx: 3,
failure: lnwire.NewInvalidOnionPayload(0, 0),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(0, 1): {
success: true,
},
getTestPair(1, 2): {
success: true,
},
getTestPair(3, 2): {},
getTestPair(3, 4): {},
},
nodeFailure: &hops[3],
},
},
// Tests an invalid onion payload in a direct peer that is also the
// final hop. The final node should be failed and the error is terminal
// since the remote node can't process our onion.
{
name: "fail invalid onion payload direct",
route: &routeOneHop,
failureSrcIdx: 1,
failure: lnwire.NewInvalidOnionPayload(0, 0),
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]pairResult{
getTestPair(1, 0): {},
},
finalFailureReason: &reasonError,
nodeFailure: &hops[1],
},
},
}
// TestResultInterpretation executes a list of test cases that test the result
@@ -192,7 +289,8 @@ func TestResultInterpretation(t *testing.T) {
}
if !reflect.DeepEqual(i, expected) {
t.Fatal("unexpected result")
t.Fatalf("unexpected result\nwant: %v\ngot: %v",
spew.Sdump(expected), spew.Sdump(i))
}
})
}