routing: include route blinding fields in blinded portion of path

This commit updates route construction to backfill the fields
required for payment to blinded paths and set amount to forward
and expiry fields to zero for intermediate hops (as is instructed
in the route blinding specification).

We could attempt to do this in the first pass, but that loop
relies on fields like amount to forward and expiry to calculate
each hop backwards, so we keep it simple (stupid) and post
processes the blinded portion, since it's computationally cheap
and more readable.
This commit is contained in:
Carla Kirk-Cohen
2022-12-20 14:03:58 -05:00
committed by Olaoluwa Osuntokun
parent c9609b8214
commit 014683ee66
4 changed files with 306 additions and 16 deletions

View File

@@ -1,6 +1,7 @@
package routing
import (
"bytes"
"container/heap"
"errors"
"fmt"
@@ -110,14 +111,23 @@ type finalHopParams struct {
// assuming the destination's feature vector signals support, otherwise this
// method will fail. If the route is too long, or the selected path cannot
// support the fully payment including fees, then a non-nil error is returned.
// If the route is to a blinded path, the blindedPath parameter is used to
// back fill additional fields that are required for a blinded payment. This is
// done in a separate pass to keep our route construction simple, as blinded
// paths require zero expiry and amount values for intermediate hops (which
// makes calculating the totals during route construction difficult if we
// include blinded paths on the first pass).
//
// NOTE: The passed slice of ChannelHops MUST be sorted in forward order: from
// the source to the target node of the path finding attempt. It is assumed that
// any feature vectors on all hops have been validated for transitive
// dependencies.
// NOTE: If a non-nil blinded path is provided it is assumed to have been
// validated by the caller.
func newRoute(sourceVertex route.Vertex,
pathEdges []*channeldb.CachedEdgePolicy, currentHeight uint32,
finalHop finalHopParams) (*route.Route, error) {
finalHop finalHopParams, blindedPath *sphinx.BlindedPath) (
*route.Route, error) {
var (
hops []*route.Hop
@@ -146,13 +156,14 @@ func newRoute(sourceVertex route.Vertex,
// contributions from the preceding hops back to the sender as
// we compute the route in reverse.
var (
amtToForward lnwire.MilliSatoshi
fee lnwire.MilliSatoshi
outgoingTimeLock uint32
tlvPayload bool
customRecords record.CustomSet
mpp *record.MPP
metadata []byte
amtToForward lnwire.MilliSatoshi
fee lnwire.MilliSatoshi
totalAmtMsatBlinded lnwire.MilliSatoshi
outgoingTimeLock uint32
tlvPayload bool
customRecords record.CustomSet
mpp *record.MPP
metadata []byte
)
// Define a helper function that checks this edge's feature
@@ -219,6 +230,10 @@ func newRoute(sourceVertex route.Vertex,
}
metadata = finalHop.metadata
if blindedPath != nil {
totalAmtMsatBlinded = finalHop.totalAmt
}
} else {
// The amount that the current hop needs to forward is
// equal to the incoming amount of the next hop.
@@ -250,6 +265,7 @@ func newRoute(sourceVertex route.Vertex,
CustomRecords: customRecords,
MPP: mpp,
Metadata: metadata,
TotalAmtMsat: totalAmtMsatBlinded,
}
hops = append([]*route.Hop{currentHop}, hops...)
@@ -260,6 +276,47 @@ func newRoute(sourceVertex route.Vertex,
nextIncomingAmount = amtToForward + fee
}
// If we are creating a route to a blinded path, we need to add some
// additional data to the route that is required for blinded forwarding.
// We do another pass on our edges to append this data.
if blindedPath != nil {
var (
inBlindedRoute bool
dataIndex = 0
introVertex = route.NewVertex(
blindedPath.IntroductionPoint,
)
)
for i, hop := range hops {
// Once we locate our introduction node, we know that
// every hop after this is part of the blinded route.
if bytes.Equal(hop.PubKeyBytes[:], introVertex[:]) {
inBlindedRoute = true
hop.BlindingPoint = blindedPath.BlindingPoint
}
// We don't need to modify edges outside of our blinded
// route.
if !inBlindedRoute {
continue
}
payload := blindedPath.BlindedHops[dataIndex].CipherText
hop.EncryptedData = payload
// All of the hops in a blinded route *except* the
// final hop should have zero amounts / time locks.
if i != len(hops)-1 {
hop.AmtToForward = 0
hop.OutgoingTimeLock = 0
}
dataIndex++
}
}
// With the base routing data expressed as hops, build the full route
newRoute, err := route.NewRouteFromHops(
nextIncomingAmount, totalTimeLock, route.Vertex(sourceVertex),