mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-19 21:31:04 +02:00
routing: bugfix for mc reporting of blinded paths
When reporting an error or a success case of a payment to a blinded paths, the amounts to forward for intermediate hops are set to 0 so we need to use the receiver amount instead.
This commit is contained in:
parent
9327940ac4
commit
decfdb68ac
@ -100,8 +100,27 @@ func interpretResult(rt *mcRoute,
|
|||||||
|
|
||||||
// processSuccess processes a successful payment attempt.
|
// processSuccess processes a successful payment attempt.
|
||||||
func (i *interpretedResult) processSuccess(route *mcRoute) {
|
func (i *interpretedResult) processSuccess(route *mcRoute) {
|
||||||
// For successes, all nodes must have acted in the right way. Therefore
|
// For successes, all nodes must have acted in the right way.
|
||||||
// we mark all of them with a success result.
|
// Therefore we mark all of them with a success result. However we need
|
||||||
|
// to handle the blinded route part separately because for intermediate
|
||||||
|
// blinded nodes the amount field is set to zero so we use the receiver
|
||||||
|
// amount.
|
||||||
|
introIdx, isBlinded := introductionPointIndex(route)
|
||||||
|
if isBlinded {
|
||||||
|
// Report success for all the pairs until the introduction
|
||||||
|
// point.
|
||||||
|
i.successPairRange(route, 0, introIdx-1)
|
||||||
|
|
||||||
|
// Handle the blinded route part.
|
||||||
|
//
|
||||||
|
// NOTE: The introIdx index here does describe the node after
|
||||||
|
// the introduction point.
|
||||||
|
i.markBlindedRouteSuccess(route, introIdx)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark nodes as successful in the non-blinded case of the payment.
|
||||||
i.successPairRange(route, 0, len(route.hops.Val)-1)
|
i.successPairRange(route, 0, len(route.hops.Val)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -517,11 +536,22 @@ func (i *interpretedResult) processPaymentOutcomeIntermediate(route *mcRoute,
|
|||||||
if introIdx == len(route.hops.Val)-1 {
|
if introIdx == len(route.hops.Val)-1 {
|
||||||
i.finalFailureReason = &reasonError
|
i.finalFailureReason = &reasonError
|
||||||
} else {
|
} else {
|
||||||
// If there are other hops between the recipient and
|
// We penalize the final hop of the blinded route which
|
||||||
// introduction node, then we just penalize the last
|
// is sufficient to not reuse this route again and is
|
||||||
// hop in the blinded route to minimize the storage of
|
// also more memory efficient because the other hops
|
||||||
// results for ephemeral keys.
|
// of the blinded path are ephemeral and will only be
|
||||||
i.failPairBalance(route, len(route.hops.Val)-1)
|
// used in conjunction with the final hop. Moreover we
|
||||||
|
// don't want to punish the introduction node because
|
||||||
|
// the blinded failure does not necessarily mean that
|
||||||
|
// the introduction node was at fault.
|
||||||
|
//
|
||||||
|
// TODO(ziggie): Make sure we only keep mc data for
|
||||||
|
// blinded paths, in both the success and failure case,
|
||||||
|
// in memory during the time of the payment and remove
|
||||||
|
// it afterwards. Blinded paths and their blinded hop
|
||||||
|
// keys are always changing per blinded route so there
|
||||||
|
// is no point in persisting this data.
|
||||||
|
i.failBlindedRoute(route)
|
||||||
}
|
}
|
||||||
|
|
||||||
// In all other cases, we penalize the reporting node. These are all
|
// In all other cases, we penalize the reporting node. These are all
|
||||||
@ -828,6 +858,41 @@ func (i *interpretedResult) successPairRange(rt *mcRoute, fromIdx, toIdx int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failBlindedRoute marks a blinded route as failed for the specific amount to
|
||||||
|
// send by only punishing the last pair.
|
||||||
|
func (i *interpretedResult) failBlindedRoute(rt *mcRoute) {
|
||||||
|
// We fail the last pair of the route, in order to fail the complete
|
||||||
|
// blinded route. This is because the combination of ephemeral pubkeys
|
||||||
|
// is unique to the route. We fail the last pair in order to not punish
|
||||||
|
// the introduction node, since we don't want to disincentivize them
|
||||||
|
// from providing that service.
|
||||||
|
pair, _ := getPair(rt, len(rt.hops.Val)-1)
|
||||||
|
|
||||||
|
// Since all the hops along a blinded path don't have any amount set, we
|
||||||
|
// extract the minimal amount to punish from the value that is tried to
|
||||||
|
// be sent to the receiver.
|
||||||
|
amt := rt.hops.Val[len(rt.hops.Val)-1].amtToFwd.Val
|
||||||
|
|
||||||
|
i.pairResults[pair] = failPairResult(amt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// markBlindedRouteSuccess marks the hops of the blinded route AFTER the
|
||||||
|
// introduction node as successful.
|
||||||
|
//
|
||||||
|
// NOTE: The introIdx must be the index of the first hop of the blinded route
|
||||||
|
// AFTER the introduction node.
|
||||||
|
func (i *interpretedResult) markBlindedRouteSuccess(rt *mcRoute, introIdx int) {
|
||||||
|
// For blinded hops we do not have the forwarding amount so we take the
|
||||||
|
// minimal amount which went through the route by looking at the last
|
||||||
|
// hop.
|
||||||
|
successAmt := rt.hops.Val[len(rt.hops.Val)-1].amtToFwd.Val
|
||||||
|
for idx := introIdx; idx < len(rt.hops.Val); idx++ {
|
||||||
|
pair, _ := getPair(rt, idx)
|
||||||
|
|
||||||
|
i.pairResults[pair] = successPairResult(successAmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// getPair returns a node pair from the route and the amount passed between that
|
// getPair returns a node pair from the route and the amount passed between that
|
||||||
// pair.
|
// pair.
|
||||||
func getPair(rt *mcRoute, channelIdx int) (DirectedNodePair,
|
func getPair(rt *mcRoute, channelIdx int) (DirectedNodePair,
|
||||||
|
@ -96,13 +96,17 @@ var (
|
|||||||
AmtToForward: 99,
|
AmtToForward: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[2],
|
PubKeyBytes: hops[2],
|
||||||
AmtToForward: 95,
|
// Intermediate blinded hops don't have an
|
||||||
|
// amount set.
|
||||||
|
AmtToForward: 0,
|
||||||
BlindingPoint: genTestPubKey(),
|
BlindingPoint: genTestPubKey(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[3],
|
PubKeyBytes: hops[3],
|
||||||
AmtToForward: 88,
|
// Intermediate blinded hops don't have an
|
||||||
|
// amount set.
|
||||||
|
AmtToForward: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[4],
|
PubKeyBytes: hops[4],
|
||||||
@ -122,8 +126,10 @@ var (
|
|||||||
AmtToForward: 99,
|
AmtToForward: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[2],
|
PubKeyBytes: hops[2],
|
||||||
AmtToForward: 95,
|
// Intermediate blinded hops don't have an
|
||||||
|
// amount set.
|
||||||
|
AmtToForward: 0,
|
||||||
BlindingPoint: genTestPubKey(),
|
BlindingPoint: genTestPubKey(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -140,13 +146,17 @@ var (
|
|||||||
TotalAmount: 100,
|
TotalAmount: 100,
|
||||||
Hops: []*route.Hop{
|
Hops: []*route.Hop{
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[1],
|
PubKeyBytes: hops[1],
|
||||||
AmtToForward: 90,
|
// Intermediate blinded hops don't have an
|
||||||
|
// amount set.
|
||||||
|
AmtToForward: 0,
|
||||||
BlindingPoint: genTestPubKey(),
|
BlindingPoint: genTestPubKey(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[2],
|
PubKeyBytes: hops[2],
|
||||||
AmtToForward: 75,
|
// Intermediate blinded hops don't have an
|
||||||
|
// amount set.
|
||||||
|
AmtToForward: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
PubKeyBytes: hops[3],
|
PubKeyBytes: hops[3],
|
||||||
@ -552,7 +562,12 @@ var resultTestCases = []resultTestCase{
|
|||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): successPairResult(100),
|
getTestPair(0, 1): successPairResult(100),
|
||||||
getTestPair(1, 2): successPairResult(99),
|
getTestPair(1, 2): successPairResult(99),
|
||||||
getTestPair(3, 4): failPairResult(88),
|
|
||||||
|
// The amount for the last hop is always the
|
||||||
|
// receiver amount because the amount to forward
|
||||||
|
// is always set to 0 for intermediate blinded
|
||||||
|
// hops.
|
||||||
|
getTestPair(3, 4): failPairResult(77),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -567,7 +582,12 @@ var resultTestCases = []resultTestCase{
|
|||||||
expectedResult: &interpretedResult{
|
expectedResult: &interpretedResult{
|
||||||
pairResults: map[DirectedNodePair]pairResult{
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
getTestPair(0, 1): successPairResult(100),
|
getTestPair(0, 1): successPairResult(100),
|
||||||
getTestPair(2, 3): failPairResult(75),
|
|
||||||
|
// The amount for the last hop is always the
|
||||||
|
// receiver amount because the amount to forward
|
||||||
|
// is always set to 0 for intermediate blinded
|
||||||
|
// hops.
|
||||||
|
getTestPair(2, 3): failPairResult(58),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -682,6 +702,25 @@ var resultTestCases = []resultTestCase{
|
|||||||
finalFailureReason: &reasonError,
|
finalFailureReason: &reasonError,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Test a multi-hop blinded route and that in a success case the amounts
|
||||||
|
// for the blinded route part are correctly set to the receiver amount.
|
||||||
|
{
|
||||||
|
name: "blinded multi-hop success",
|
||||||
|
route: blindedMultiToIntroduction,
|
||||||
|
success: true,
|
||||||
|
expectedResult: &interpretedResult{
|
||||||
|
pairResults: map[DirectedNodePair]pairResult{
|
||||||
|
getTestPair(0, 1): successPairResult(100),
|
||||||
|
|
||||||
|
// For the route blinded part of the route the
|
||||||
|
// success amount is determined by the receiver
|
||||||
|
// amount because the intermediate blinded hops
|
||||||
|
// set the forwarded amount to 0.
|
||||||
|
getTestPair(1, 2): successPairResult(58),
|
||||||
|
getTestPair(2, 3): successPairResult(58),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestResultInterpretation executes a list of test cases that test the result
|
// TestResultInterpretation executes a list of test cases that test the result
|
||||||
|
Loading…
x
Reference in New Issue
Block a user