multi: add blinded route to route requests expressed as hints
Add the option to include a blinded route in a route request (exclusive to including hop hints, because it's incongruous to include both), and express the route as a chain of hop hints. Using a chain of hints over a single hint to represent the whole path allows us to re-use our route construction to fill in a lot of the path on our behalf.
This commit is contained in:
1 parent
48e36d93d4
commit
c9609b8214
7 files changed
+501
-18
No files matched your search
+135
-11
@@ -91,6 +91,34 @@ var (
|
||||
// ErrRouterShuttingDown is returned if the router is in the process of
|
||||
// shutting down.
|
||||
ErrRouterShuttingDown = fmt.Errorf("router shutting down")
|
||||
|
||||
// ErrSelfIntro is a failure returned when the source node of a
|
||||
// route request is also the introduction node. This is not yet
|
||||
// supported because LND does not support blinded forwardingg.
|
||||
ErrSelfIntro = errors.New("introduction point as own node not " +
|
||||
"supported")
|
||||
|
||||
// ErrHintsAndBlinded is returned if a route request has both
|
||||
// bolt 11 route hints and a blinded path set.
|
||||
ErrHintsAndBlinded = errors.New("bolt 11 route hints and blinded " +
|
||||
"paths are mutually exclusive")
|
||||
|
||||
// ErrExpiryAndBlinded is returned if a final cltv and a blinded path
|
||||
// are provided, as the cltv should be provided within the blinded
|
||||
// path.
|
||||
ErrExpiryAndBlinded = errors.New("final cltv delta and blinded " +
|
||||
"paths are mutually exclusive")
|
||||
|
||||
// ErrTargetAndBlinded is returned is a target destination and a
|
||||
// blinded path are both set (as the target is inferred from the
|
||||
// blinded path).
|
||||
ErrTargetAndBlinded = errors.New("target node and blinded paths " +
|
||||
"are mutually exclusive")
|
||||
|
||||
// ErrNoTarget is returned when the target node for a route is not
|
||||
// provided by either a blinded route or a cleartext pubkey.
|
||||
ErrNoTarget = errors.New("destination not set in target or blinded " +
|
||||
"path")
|
||||
)
|
||||
|
||||
// ChannelGraphSource represents the source of information about the topology
|
||||
@@ -1809,12 +1837,16 @@ type routingMsg struct {
|
||||
err chan error
|
||||
}
|
||||
|
||||
// RouteRequest contains the parameters for a pathfinding request.
|
||||
// RouteRequest contains the parameters for a pathfinding request. It may
|
||||
// describe a request to make a regular payment or one to a blinded path
|
||||
// (incdicated by a non-nil BlindedPayment field).
|
||||
type RouteRequest struct {
|
||||
// Source is the node that the path originates from.
|
||||
Source route.Vertex
|
||||
|
||||
// Target is the node that the path terminates at.
|
||||
// Target is the node that the path terminates at. If the route
|
||||
// includes a blinded path, target will be the blinded node id of the
|
||||
// final hop in the blinded route.
|
||||
Target route.Vertex
|
||||
|
||||
// Amount is the Amount in millisatoshis to be delivered to the target
|
||||
@@ -1834,39 +1866,131 @@ type RouteRequest struct {
|
||||
CustomRecords record.CustomSet
|
||||
|
||||
// RouteHints contains an additional set of edges to include in our
|
||||
// view of the graph.
|
||||
// view of the graph. This may either be a set of hints for private
|
||||
// channels or a "virtual" hop hint that represents a blinded route.
|
||||
RouteHints RouteHints
|
||||
|
||||
// FinalExpiry is the cltv delta for the final hop.
|
||||
// FinalExpiry is the cltv delta for the final hop. If paying to a
|
||||
// blinded path, this value is a duplicate of the delta provided
|
||||
// in blinded payment.
|
||||
FinalExpiry uint16
|
||||
|
||||
// BlindedPayment contains an optional blinded path and parameters
|
||||
// used to reach a target node via a blinded path. This field is
|
||||
// mutually exclusive with the Target field.
|
||||
BlindedPayment *BlindedPayment
|
||||
}
|
||||
|
||||
// RouteHints is an alias type for a set of route hints, with the source node
|
||||
// as the map's key and the details of the hint(s) in the edge policy.
|
||||
type RouteHints map[route.Vertex][]*channeldb.CachedEdgePolicy
|
||||
|
||||
// NewRouteRequest produces a new route request.
|
||||
// NewRouteRequest produces a new route request for a regular payment or one
|
||||
// to a blinded route, validating that the target, routeHints and finalExpiry
|
||||
// parameters are mutually exclusive with the blindedPayment parameter (which
|
||||
// contains these values for blinded payments).
|
||||
func NewRouteRequest(source route.Vertex, target *route.Vertex,
|
||||
amount lnwire.MilliSatoshi, timePref float64,
|
||||
restrictions *RestrictParams, customRecords record.CustomSet,
|
||||
routeHints RouteHints, finalExpiry uint16) (*RouteRequest, error) {
|
||||
routeHints RouteHints, blindedPayment *BlindedPayment,
|
||||
finalExpiry uint16) (*RouteRequest, error) {
|
||||
|
||||
if target == nil {
|
||||
return nil, errors.New("target node required")
|
||||
var (
|
||||
// Assume that we're starting off with a regular payment.
|
||||
requestHints = routeHints
|
||||
requestExpiry = finalExpiry
|
||||
)
|
||||
|
||||
if blindedPayment != nil {
|
||||
if err := blindedPayment.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("invalid blinded payment: %w",
|
||||
err)
|
||||
}
|
||||
|
||||
introVertex := route.NewVertex(
|
||||
blindedPayment.BlindedPath.IntroductionPoint,
|
||||
)
|
||||
if source == introVertex {
|
||||
return nil, ErrSelfIntro
|
||||
}
|
||||
|
||||
// Check that the values for a clear path have not been set,
|
||||
// as this is an ambiguous signal from the caller.
|
||||
if routeHints != nil {
|
||||
return nil, ErrHintsAndBlinded
|
||||
}
|
||||
|
||||
if finalExpiry != 0 {
|
||||
return nil, ErrExpiryAndBlinded
|
||||
}
|
||||
|
||||
// If we have a blinded path with 1 hop, the cltv expiry
|
||||
// will not be included in any hop hints (since we're just
|
||||
// sending to the introduction node and need no blinded hints).
|
||||
// In this case, we include it to make sure that the final
|
||||
// cltv delta is accounted for (since it's part of the blinded
|
||||
// delta). In the case of a multi-hop route, we set our final
|
||||
// cltv to zero, since it's going to be accounted for in the
|
||||
// delta for our hints.
|
||||
if len(blindedPayment.BlindedPath.BlindedHops) == 1 {
|
||||
requestExpiry = blindedPayment.CltvExpiryDelta
|
||||
}
|
||||
|
||||
requestHints = blindedPayment.toRouteHints()
|
||||
}
|
||||
|
||||
requestTarget, err := getTargetNode(target, blindedPayment)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RouteRequest{
|
||||
Source: source,
|
||||
Target: *target,
|
||||
Target: requestTarget,
|
||||
Amount: amount,
|
||||
TimePreference: timePref,
|
||||
Restrictions: restrictions,
|
||||
CustomRecords: customRecords,
|
||||
RouteHints: routeHints,
|
||||
FinalExpiry: finalExpiry,
|
||||
RouteHints: requestHints,
|
||||
FinalExpiry: requestExpiry,
|
||||
BlindedPayment: blindedPayment,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getTargetNode(target *route.Vertex, blindedPayment *BlindedPayment) (
|
||||
route.Vertex, error) {
|
||||
|
||||
var (
|
||||
blinded = blindedPayment != nil
|
||||
targetSet = target != nil
|
||||
)
|
||||
|
||||
switch {
|
||||
case blinded && targetSet:
|
||||
return route.Vertex{}, ErrTargetAndBlinded
|
||||
|
||||
case blinded:
|
||||
// If we're dealing with an edge-case blinded path that just
|
||||
// has an introduction node (first hop expected to be the intro
|
||||
// hop), then we return the unblinded introduction node as our
|
||||
// target.
|
||||
hops := blindedPayment.BlindedPath.BlindedHops
|
||||
if len(hops) == 1 {
|
||||
return route.NewVertex(
|
||||
blindedPayment.BlindedPath.IntroductionPoint,
|
||||
), nil
|
||||
}
|
||||
|
||||
return route.NewVertex(hops[len(hops)-1].BlindedNodePub), nil
|
||||
|
||||
case targetSet:
|
||||
return *target, nil
|
||||
|
||||
default:
|
||||
return route.Vertex{}, ErrNoTarget
|
||||
}
|
||||
}
|
||||
|
||||
// FindRoute attempts to query the ChannelRouter for the optimum path to a
|
||||
// particular target destination to which it is able to send `amt` after
|
||||
// factoring in channel capacities and cumulative fees along the route.
|
||||
|
||||
Reference in new issue
Block a user