routing: track unifiedPolicyEdge

Preparation so that we can have the inbound fee available
in addition to the outgoing policy.
This commit is contained in:
Joost Jager 2022-09-03 13:59:10 +02:00
parent a6d4bb5c89
commit e0a080454a
6 changed files with 64 additions and 51 deletions

View File

@ -3,7 +3,6 @@ package routing
import (
"container/heap"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/route"
)
@ -39,7 +38,7 @@ type nodeWithDist struct {
weight int64
// nextHop is the edge this route comes from.
nextHop *models.CachedEdgePolicy
nextHop *unifiedEdge
// routingInfoSize is the total size requirement for the payloads field
// in the onion packet from this hop towards the final destination.

View File

@ -11,7 +11,6 @@ import (
"github.com/btcsuite/btcd/btcutil"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/record"
@ -50,7 +49,7 @@ const (
type pathFinder = func(g *graphParams, r *RestrictParams,
cfg *PathFindingConfig, source, target route.Vertex,
amt lnwire.MilliSatoshi, timePref float64, finalHtlcExpiry int32) (
[]*models.CachedEdgePolicy, float64, error)
[]*unifiedEdge, float64, error)
var (
// DefaultEstimator is the default estimator used for computing
@ -126,7 +125,7 @@ type finalHopParams struct {
// 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 []*models.CachedEdgePolicy, currentHeight uint32,
pathEdges []*unifiedEdge, currentHeight uint32,
finalHop finalHopParams, blindedPath *sphinx.BlindedPath) (
*route.Route, error) {
@ -149,7 +148,7 @@ func newRoute(sourceVertex route.Vertex,
for i := pathLength - 1; i >= 0; i-- {
// Now we'll start to calculate the items within the per-hop
// payload for the hop this edge is leading to.
edge := pathEdges[i]
edge := pathEdges[i].policy
// We'll calculate the amounts, timelocks, and fees for each hop
// in the route. The base case is the final hop which includes
@ -245,13 +244,15 @@ func newRoute(sourceVertex route.Vertex,
// and its policy for the outgoing channel. This policy
// is stored as part of the incoming channel of
// the next hop.
fee = pathEdges[i+1].ComputeFee(amtToForward)
fee = pathEdges[i+1].policy.ComputeFee(amtToForward)
// We'll take the total timelock of the preceding hop as
// the outgoing timelock or this hop. Then we'll
// increment the total timelock incurred by this hop.
outgoingTimeLock = totalTimeLock
totalTimeLock += uint32(pathEdges[i+1].TimeLockDelta)
totalTimeLock += uint32(
pathEdges[i+1].policy.TimeLockDelta,
)
}
// Since we're traversing the path backwards atm, we prepend
@ -504,7 +505,7 @@ func getOutgoingBalance(node route.Vertex, outgoingChans map[uint64]struct{},
// available bandwidth.
func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
finalHtlcExpiry int32) ([]*models.CachedEdgePolicy, float64, error) {
finalHtlcExpiry int32) ([]*unifiedEdge, float64, error) {
// Pathfinding can be a significant portion of the total payment
// latency, especially on low-powered devices. Log several metrics to
@ -859,7 +860,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
amountToReceive: amountToReceive,
incomingCltv: incomingCltv,
probability: probability,
nextHop: edge.policy,
nextHop: edge,
routingInfoSize: routingInfoSize,
}
distance[fromVertex] = withDist
@ -1009,7 +1010,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// Use the distance map to unravel the forward path from source to
// target.
var pathEdges []*models.CachedEdgePolicy
var pathEdges []*unifiedEdge
currentNode := source
for {
// Determine the next hop forward using the next map.
@ -1024,7 +1025,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
pathEdges = append(pathEdges, currentNodeWithDist.nextHop)
// Advance current node.
currentNode = currentNodeWithDist.nextHop.ToNodePubKey()
currentNode = currentNodeWithDist.nextHop.policy.ToNodePubKey()
// Check stop condition at the end of this loop. This prevents
// breaking out too soon for self-payments that have target set
@ -1045,7 +1046,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// route construction does not care where the features are actually
// taken from. In the future we may wish to do route construction within
// findPath, and avoid using ChannelEdgePolicy altogether.
pathEdges[len(pathEdges)-1].ToNodeFeatures = features
pathEdges[len(pathEdges)-1].policy.ToNodeFeatures = features
log.Debugf("Found route: probability=%v, hops=%v, fee=%v",
distance[source].probability, len(pathEdges),

View File

@ -1225,7 +1225,7 @@ func runPathFindingWithAdditionalEdges(t *testing.T, useCache bool) {
}
find := func(r *RestrictParams) (
[]*models.CachedEdgePolicy, error) {
[]*unifiedEdge, error) {
return dbFindPath(
graph.graph, additionalEdges, &mockBandwidthHints{},
@ -1437,7 +1437,7 @@ func runPathFindingWithRedundantAdditionalEdges(t *testing.T, useCache bool) {
require.NoError(t, err, "unable to find path to bob")
require.Len(t, path, 1)
require.Equal(t, realChannelID, path[0].ChannelID,
require.Equal(t, realChannelID, path[0].policy.ChannelID,
"additional edge for known edge wasn't ignored")
}
@ -1723,8 +1723,17 @@ func TestNewRoute(t *testing.T) {
}
t.Run(testCase.name, func(t *testing.T) {
var unifiedHops []*unifiedEdge
for _, hop := range testCase.hops {
unifiedHops = append(unifiedHops,
&unifiedEdge{
policy: hop,
},
)
}
route, err := newRoute(
sourceVertex, testCase.hops, startingHeight,
sourceVertex, unifiedHops, startingHeight,
finalHopParams{
amt: testCase.paymentAmount,
totalAmt: testCase.paymentAmount,
@ -1864,7 +1873,7 @@ func runDestTLVGraphFallback(t *testing.T, useCache bool) {
require.NoError(t, err, "unable to fetch source node")
find := func(r *RestrictParams,
target route.Vertex) ([]*models.CachedEdgePolicy, error) {
target route.Vertex) ([]*unifiedEdge, error) {
return dbFindPath(
ctx.graph, nil, &mockBandwidthHints{},
@ -2522,7 +2531,7 @@ func TestPathFindSpecExample(t *testing.T) {
}
func assertExpectedPath(t *testing.T, aliasMap map[string]route.Vertex,
path []*models.CachedEdgePolicy, nodeAliases ...string) {
path []*unifiedEdge, nodeAliases ...string) {
if len(path) != len(nodeAliases) {
t.Fatalf("number of hops=(%v) and number of aliases=(%v) do "+
@ -2530,9 +2539,10 @@ func assertExpectedPath(t *testing.T, aliasMap map[string]route.Vertex,
}
for i, hop := range path {
if hop.ToNodePubKey() != aliasMap[nodeAliases[i]] {
if hop.policy.ToNodePubKey() != aliasMap[nodeAliases[i]] {
t.Fatalf("expected %v to be pos #%v in hop, instead "+
"%v was", nodeAliases[i], i, hop.ToNodePubKey())
"%v was", nodeAliases[i], i,
hop.policy.ToNodePubKey())
}
}
}
@ -2606,10 +2616,10 @@ func runRestrictOutgoingChannel(t *testing.T, useCache bool) {
// Assert that the route starts with channel chanSourceB1, in line with
// the specified restriction.
if path[0].ChannelID != chanSourceB1 {
if path[0].policy.ChannelID != chanSourceB1 {
t.Fatalf("expected route to pass through channel %v, "+
"but channel %v was selected instead", chanSourceB1,
path[0].ChannelID)
path[0].policy.ChannelID)
}
// If a direct channel to target is allowed as well, that channel is
@ -2619,7 +2629,7 @@ func runRestrictOutgoingChannel(t *testing.T, useCache bool) {
}
path, err = ctx.findPath(target, paymentAmt)
require.NoError(t, err, "unable to find path")
if path[0].ChannelID != chanSourceTarget {
if path[0].policy.ChannelID != chanSourceTarget {
t.Fatalf("expected route to pass through channel %v",
chanSourceTarget)
}
@ -2658,10 +2668,10 @@ func runRestrictLastHop(t *testing.T, useCache bool) {
ctx.restrictParams.LastHop = &lastHop
path, err := ctx.findPath(target, paymentAmt)
require.NoError(t, err, "unable to find path")
if path[0].ChannelID != 3 {
if path[0].policy.ChannelID != 3 {
t.Fatalf("expected route to pass through channel 3, "+
"but channel %v was selected instead",
path[0].ChannelID)
path[0].policy.ChannelID)
}
}
@ -2941,10 +2951,10 @@ func testProbabilityRouting(t *testing.T, useCache bool,
}
// Assert that the route passes through the expected channel.
if path[1].ChannelID != expectedChan {
if path[1].policy.ChannelID != expectedChan {
t.Fatalf("expected route to pass through channel %v, "+
"but channel %v was selected instead", expectedChan,
path[1].ChannelID)
path[1].policy.ChannelID)
}
}
@ -3005,10 +3015,10 @@ func runEqualCostRouteSelection(t *testing.T, useCache bool) {
t.Fatal(err)
}
if path[1].ChannelID != 2 {
if path[1].policy.ChannelID != 2 {
t.Fatalf("expected route to pass through channel %v, "+
"but channel %v was selected instead", 2,
path[1].ChannelID)
path[1].policy.ChannelID)
}
}
@ -3168,7 +3178,7 @@ func (c *pathFindingTestContext) aliasFromKey(pubKey route.Vertex) string {
}
func (c *pathFindingTestContext) findPath(target route.Vertex,
amt lnwire.MilliSatoshi) ([]*models.CachedEdgePolicy,
amt lnwire.MilliSatoshi) ([]*unifiedEdge,
error) {
return dbFindPath(
@ -3177,7 +3187,7 @@ func (c *pathFindingTestContext) findPath(target route.Vertex,
)
}
func (c *pathFindingTestContext) assertPath(path []*models.CachedEdgePolicy,
func (c *pathFindingTestContext) assertPath(path []*unifiedEdge,
expected []uint64) {
if len(path) != len(expected) {
@ -3186,9 +3196,10 @@ func (c *pathFindingTestContext) assertPath(path []*models.CachedEdgePolicy,
}
for i, edge := range path {
if edge.ChannelID != expected[i] {
if edge.policy.ChannelID != expected[i] {
c.t.Fatalf("expected hop %v to be channel %v, "+
"but got %v", i, expected[i], edge.ChannelID)
"but got %v", i, expected[i],
edge.policy.ChannelID)
}
}
}
@ -3200,7 +3211,7 @@ func dbFindPath(graph *channeldb.ChannelGraph,
bandwidthHints bandwidthHints,
r *RestrictParams, cfg *PathFindingConfig,
source, target route.Vertex, amt lnwire.MilliSatoshi, timePref float64,
finalHtlcExpiry int32) ([]*models.CachedEdgePolicy, error) {
finalHtlcExpiry int32) ([]*unifiedEdge, error) {
sourceNode, err := graph.SourceNode()
if err != nil {
@ -3351,11 +3362,11 @@ func TestBlindedRouteConstruction(t *testing.T) {
carolDaveEdge := blindedEdges[carolVertex][0]
daveEveEdge := blindedEdges[daveBlindedVertex][0]
edges := []*models.CachedEdgePolicy{
aliceBobEdge,
bobCarolEdge,
carolDaveEdge.EdgePolicy(),
daveEveEdge.EdgePolicy(),
edges := []*unifiedEdge{
{policy: aliceBobEdge},
{policy: bobCarolEdge},
{policy: carolDaveEdge.EdgePolicy()},
{policy: daveEveEdge.EdgePolicy()},
}
// Total timelock for the route should include:

View File

@ -212,7 +212,7 @@ func TestRequestRoute(t *testing.T) {
// Override pathfinder with a mock.
session.pathFinder = func(_ *graphParams, r *RestrictParams,
_ *PathFindingConfig, _, _ route.Vertex, _ lnwire.MilliSatoshi,
_ float64, _ int32) ([]*models.CachedEdgePolicy, float64,
_ float64, _ int32) ([]*unifiedEdge, float64,
error) {
// We expect find path to receive a cltv limit excluding the
@ -221,14 +221,16 @@ func TestRequestRoute(t *testing.T) {
t.Fatal("wrong cltv limit")
}
path := []*models.CachedEdgePolicy{
path := []*unifiedEdge{
{
ToNodePubKey: func() route.Vertex {
return route.Vertex{}
policy: &models.CachedEdgePolicy{
ToNodePubKey: func() route.Vertex {
return route.Vertex{}
},
ToNodeFeatures: lnwire.NewFeatureVector(
nil, nil,
),
},
ToNodeFeatures: lnwire.NewFeatureVector(
nil, nil,
),
},
}

View File

@ -3218,14 +3218,14 @@ func getRouteUnifiers(source route.Vertex, hops []route.Vertex,
// including fees, to send the payment.
func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
unifiers []*edgeUnifier, bandwidthHints *bandwidthManager,
hops []route.Vertex) ([]*models.CachedEdgePolicy,
hops []route.Vertex) ([]*unifiedEdge,
lnwire.MilliSatoshi, error) {
// Now that we arrived at the start of the route and found out the route
// total amount, we make a forward pass. Because the amount may have
// been increased in the backward pass, fees need to be recalculated and
// amount ranges re-checked.
var pathEdges []*models.CachedEdgePolicy
var pathEdges []*unifiedEdge
for i, unifier := range unifiers {
edge := unifier.getEdge(receiverAmt, bandwidthHints)
if edge == nil {
@ -3247,7 +3247,7 @@ func getPathEdges(source route.Vertex, receiverAmt lnwire.MilliSatoshi,
)
}
pathEdges = append(pathEdges, edge.policy)
pathEdges = append(pathEdges, edge)
}
return pathEdges, receiverAmt, nil

View File

@ -2450,8 +2450,8 @@ func TestFindPathFeeWeighting(t *testing.T) {
if len(path) != 1 {
t.Fatalf("expected path length of 1, instead was: %v", len(path))
}
if path[0].ToNodePubKey() != ctx.aliases["luoji"] {
t.Fatalf("wrong node: %v", path[0].ToNodePubKey())
if path[0].policy.ToNodePubKey() != ctx.aliases["luoji"] {
t.Fatalf("wrong node: %v", path[0].policy.ToNodePubKey())
}
}