routing: penalize all node pairs for unknown outcomes
When an undecryptable failure comes back for a payment attempt, we previously only penalized our own outgoing connection. However, any node could have caused this failure. It is therefore better to penalize all node connections along the route. Then at least we know for sure that we will hit the responsible node.
This commit is contained in:
parent
c39d7a29cd
commit
e135cf7326
@ -7,6 +7,11 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Instantiate variables to allow taking a reference from the failure reason.
|
||||||
|
var (
|
||||||
|
reasonError = channeldb.FailureReasonError
|
||||||
|
)
|
||||||
|
|
||||||
// interpretedResult contains the result of the interpretation of a payment
|
// interpretedResult contains the result of the interpretation of a payment
|
||||||
// outcome.
|
// outcome.
|
||||||
type interpretedResult struct {
|
type interpretedResult struct {
|
||||||
@ -52,21 +57,14 @@ func (i *interpretedResult) processFail(
|
|||||||
rt *route.Route, errSourceIdx *int,
|
rt *route.Route, errSourceIdx *int,
|
||||||
failure lnwire.FailureMessage) (bool, channeldb.FailureReason) {
|
failure lnwire.FailureMessage) (bool, channeldb.FailureReason) {
|
||||||
|
|
||||||
var failureSourceIdxInt int
|
|
||||||
|
|
||||||
if errSourceIdx == nil {
|
if errSourceIdx == nil {
|
||||||
// If the failure message could not be decrypted, attribute the
|
i.processPaymentOutcomeUnknown(rt)
|
||||||
// failure to our own outgoing channel.
|
return false, 0
|
||||||
//
|
|
||||||
// TODO(joostager): Penalize all channels in the route.
|
|
||||||
failureSourceIdxInt = 0
|
|
||||||
failure = lnwire.NewTemporaryChannelFailure(nil)
|
|
||||||
} else {
|
|
||||||
failureSourceIdxInt = *errSourceIdx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var failureVertex route.Vertex
|
var failureVertex route.Vertex
|
||||||
|
|
||||||
|
failureSourceIdxInt := *errSourceIdx
|
||||||
if failureSourceIdxInt > 0 {
|
if failureSourceIdxInt > 0 {
|
||||||
failureVertex = rt.Hops[failureSourceIdxInt-1].PubKeyBytes
|
failureVertex = rt.Hops[failureSourceIdxInt-1].PubKeyBytes
|
||||||
} else {
|
} else {
|
||||||
@ -241,6 +239,74 @@ func (i *interpretedResult) processFail(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processPaymentOutcomeUnknown processes a payment outcome for which no failure
|
||||||
|
// message or source is available.
|
||||||
|
func (i *interpretedResult) processPaymentOutcomeUnknown(route *route.Route) {
|
||||||
|
n := len(route.Hops)
|
||||||
|
|
||||||
|
// If this is a direct payment, the destination must be at fault.
|
||||||
|
if n == 1 {
|
||||||
|
i.failNode(route, n)
|
||||||
|
i.finalFailureReason = &reasonError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise penalize all channels in the route to make sure the
|
||||||
|
// responsible node is at least hit too. We even penalize the connection
|
||||||
|
// to our own peer, because that peer could also be responsible.
|
||||||
|
i.failPairRange(route, 0, n-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// failNode marks the node indicated by idx in the route as failed. This
|
||||||
|
// function intentionally panics when the self node is failed.
|
||||||
|
func (i *interpretedResult) failNode(rt *route.Route, idx int) {
|
||||||
|
i.nodeFailure = &rt.Hops[idx-1].PubKeyBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// failPairRange marks the node pairs from node fromIdx to node toIdx as failed.
|
||||||
|
func (i *interpretedResult) failPairRange(
|
||||||
|
rt *route.Route, fromIdx, toIdx int) {
|
||||||
|
|
||||||
|
for idx := fromIdx; idx <= toIdx; idx++ {
|
||||||
|
i.failPair(rt, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failPair marks a pair as failed in both directions.
|
||||||
|
func (i *interpretedResult) failPair(
|
||||||
|
rt *route.Route, idx int) {
|
||||||
|
|
||||||
|
pair, _ := getPair(rt, idx)
|
||||||
|
|
||||||
|
// Report pair in both directions without a minimum penalization amount.
|
||||||
|
i.pairResults[pair] = 0
|
||||||
|
i.pairResults[pair.Reverse()] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPair returns a node pair from the route and the amount passed between that
|
||||||
|
// pair.
|
||||||
|
func getPair(rt *route.Route, channelIdx int) (DirectedNodePair,
|
||||||
|
lnwire.MilliSatoshi) {
|
||||||
|
|
||||||
|
nodeTo := rt.Hops[channelIdx].PubKeyBytes
|
||||||
|
var (
|
||||||
|
nodeFrom route.Vertex
|
||||||
|
amt lnwire.MilliSatoshi
|
||||||
|
)
|
||||||
|
|
||||||
|
if channelIdx == 0 {
|
||||||
|
nodeFrom = rt.SourcePubKey
|
||||||
|
amt = rt.TotalAmount
|
||||||
|
} else {
|
||||||
|
nodeFrom = rt.Hops[channelIdx-1].PubKeyBytes
|
||||||
|
amt = rt.Hops[channelIdx-1].AmtToForward
|
||||||
|
}
|
||||||
|
|
||||||
|
pair := NewDirectedNodePair(nodeFrom, nodeTo)
|
||||||
|
|
||||||
|
return pair, amt
|
||||||
|
}
|
||||||
|
|
||||||
// getFailedPair tries to locate the failing pair given a route and the pubkey
|
// getFailedPair tries to locate the failing pair given a route and the pubkey
|
||||||
// of the node that sent the failure. It will assume that the failure is
|
// of the node that sent the failure. It will assume that the failure is
|
||||||
// associated with the outgoing channel set of the failing node. As a second
|
// associated with the outgoing channel set of the failing node. As a second
|
||||||
|
Loading…
Reference in New Issue
Block a user