routing: stricter payment result interpretation

This commit overhauls the interpretation of failed payments. It changes
the interpretation rules so that we always apply the strongest possible
set of penalties, without making assumptions that would hurt good nodes.

Main changes are:

- Apply different rule sets for intermediate and final nodes. Both types
of nodes have different sets of failures that we expect. Penalize nodes
that send unexpected failure messages.

- Distinguish between direct payments and multi-hop payments. For direct
payments, we can infer more about the performance of our peer because we
trust ourselves.

- In many cases it is impossible for the sender to determine which of
the two nodes in a pair is responsible for the failure. In this
situation, we now penalize bidirectionally. This does not hurt the good
node of the pair, because only its connection to a bad node is
penalized.

- Previously we always penalized the outgoing connection of the
reporting node. This is incorrect for policy related failures. For
policy related failures, it could also be that the reporting node
received a wrongly crafted htlc from its predecessor. By penalizing the
incoming channel, we surely hit the responsible node.

- FailExpiryTooSoon is a failure that could have been caused by any node
up to the reporting node by delaying forwarding of the htlc. We don't
know which node is responsible, therefore we now penalize all node pairs
in the route.
This commit is contained in:
Joost Jager committed 2019-08-17 10:24:09 +02:00
1 parent e7a457f1ce
commit d9ec158412
2 files changed
+280 -208

No files matched your search

+47 -2
View File
@@ -14,6 +14,14 @@ var (
{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4},
}
routeOneHop = route.Route{
SourcePubKey: hops[0],
TotalAmount: 100,
Hops: []*route.Hop{
{PubKeyBytes: hops[1], AmtToForward: 99},
},
}
routeTwoHop = route.Route{
SourcePubKey: hops[0],
TotalAmount: 100,
@@ -35,6 +43,10 @@ var (
}
)
func getTestPair(from, to int) DirectedNodePair {
return NewDirectedNodePair(hops[from], hops[to])
}
type resultTestCase struct {
name string
route *route.Route
@@ -55,7 +67,7 @@ var resultTestCases = []resultTestCase{
expectedResult: &interpretedResult{
pairResults: map[DirectedNodePair]lnwire.MilliSatoshi{
NewDirectedNodePair(hops[1], hops[2]): 99,
getTestPair(1, 2): 99,
},
},
},
@@ -68,7 +80,40 @@ var resultTestCases = []resultTestCase{
failure: lnwire.NewExpiryTooSoon(lnwire.ChannelUpdate{}),
expectedResult: &interpretedResult{
nodeFailure: &hops[3],
pairResults: map[DirectedNodePair]lnwire.MilliSatoshi{
getTestPair(0, 1): 0,
getTestPair(1, 0): 0,
getTestPair(1, 2): 0,
getTestPair(2, 1): 0,
getTestPair(2, 3): 0,
getTestPair(3, 2): 0,
},
},
},
// Tests a malformed htlc from a direct peer.
{
name: "fail malformed htlc from direct peer",
route: &routeTwoHop,
failureSrcIdx: 0,
failure: lnwire.NewInvalidOnionKey(nil),
expectedResult: &interpretedResult{
nodeFailure: &hops[1],
},
},
// Tests a malformed htlc from a direct peer that is also the final
// destination.
{
name: "fail malformed htlc from direct final peer",
route: &routeOneHop,
failureSrcIdx: 0,
failure: lnwire.NewInvalidOnionKey(nil),
expectedResult: &interpretedResult{
finalFailureReason: &reasonError,
nodeFailure: &hops[1],
},
},
}