routing: take Vertex types as path finding source and target nodes

Currently public keys are represented either as a 33-byte array (Vertex) or as a
btcec.PublicKey struct. The latter isn't useable as index into maps and
cannot be used easily in compares. Therefore the 33-byte array
representation is used predominantly throughout the code base.

This commit converts the argument types of source and target nodes for
path finding to Vertex. Path finding executes no crypto operations and
using Vertex simplifies the code.

Additionally, it prepares for the path finding source parameter to be
exposed over rpc in a follow up commit without requiring conversion back
and forth between Vertex and btcec.PublicKey.
This commit is contained in:
Joost Jager 2019-03-05 16:55:19 +01:00
parent b09adc3219
commit 7719bc432f
No known key found for this signature in database
GPG Key ID: A61B9D4C393C59C7
7 changed files with 198 additions and 171 deletions

@ -153,7 +153,7 @@ func (m *missionControl) GraphPruneView() graphPruneView {
// in order to populate additional edges to explore when finding a path to the // in order to populate additional edges to explore when finding a path to the
// payment's destination. // payment's destination.
func (m *missionControl) NewPaymentSession(routeHints [][]HopHint, func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
target *btcec.PublicKey) (*paymentSession, error) { target Vertex) (*paymentSession, error) {
viewSnapshot := m.GraphPruneView() viewSnapshot := m.GraphPruneView()
@ -175,7 +175,13 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
if i != len(routeHint)-1 { if i != len(routeHint)-1 {
endNode.AddPubKey(routeHint[i+1].NodeID) endNode.AddPubKey(routeHint[i+1].NodeID)
} else { } else {
endNode.AddPubKey(target) targetPubKey, err := btcec.ParsePubKey(
target[:], btcec.S256(),
)
if err != nil {
return nil, err
}
endNode.AddPubKey(targetPubKey)
} }
// Finally, create the channel edge from the hop hint // Finally, create the channel edge from the hop hint

@ -1,7 +1,6 @@
package routing package routing
import ( import (
"bytes"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math" "math"
@ -429,8 +428,7 @@ type RestrictParams struct {
// destination node back to source. This is to properly accumulate fees // destination node back to source. This is to properly accumulate fees
// that need to be paid along the path and accurately check the amount // that need to be paid along the path and accurately check the amount
// to forward at every node against the available bandwidth. // to forward at every node against the available bandwidth.
func findPath(g *graphParams, r *RestrictParams, func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) { amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, error) {
var err error var err error
@ -491,17 +489,14 @@ func findPath(g *graphParams, r *RestrictParams,
} }
} }
sourceVertex := Vertex(sourceNode.PubKeyBytes)
// We can't always assume that the end destination is publicly // We can't always assume that the end destination is publicly
// advertised to the network and included in the graph.ForEachNode call // advertised to the network and included in the graph.ForEachNode call
// above, so we'll manually include the target node. The target node // above, so we'll manually include the target node. The target node
// charges no fee. Distance is set to 0, because this is the starting // charges no fee. Distance is set to 0, because this is the starting
// point of the graph traversal. We are searching backwards to get the // point of the graph traversal. We are searching backwards to get the
// fees first time right and correctly match channel bandwidth. // fees first time right and correctly match channel bandwidth.
targetVertex := NewVertex(target) targetNode := &channeldb.LightningNode{PubKeyBytes: target}
targetNode := &channeldb.LightningNode{PubKeyBytes: targetVertex} distance[target] = nodeWithDist{
distance[targetVertex] = nodeWithDist{
dist: 0, dist: 0,
node: targetNode, node: targetNode,
amountToReceive: amt, amountToReceive: amt,
@ -534,7 +529,7 @@ func findPath(g *graphParams, r *RestrictParams,
// skip it. // skip it.
// TODO(halseth): also ignore disable flags for non-local // TODO(halseth): also ignore disable flags for non-local
// channels if bandwidth hint is set? // channels if bandwidth hint is set?
isSourceChan := fromVertex == sourceVertex isSourceChan := fromVertex == source
edgeFlags := edge.ChannelFlags edgeFlags := edge.ChannelFlags
isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0 isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0
@ -597,7 +592,7 @@ func findPath(g *graphParams, r *RestrictParams,
// node, no additional timelock is required. // node, no additional timelock is required.
var fee lnwire.MilliSatoshi var fee lnwire.MilliSatoshi
var timeLockDelta uint16 var timeLockDelta uint16
if fromVertex != sourceVertex { if fromVertex != source {
fee = computeFee(amountToSend, edge) fee = computeFee(amountToSend, edge)
timeLockDelta = edge.TimeLockDelta timeLockDelta = edge.TimeLockDelta
} }
@ -666,7 +661,7 @@ func findPath(g *graphParams, r *RestrictParams,
// To start, our target node will the sole item within our distance // To start, our target node will the sole item within our distance
// heap. // heap.
heap.Push(&nodeHeap, distance[targetVertex]) heap.Push(&nodeHeap, distance[target])
for nodeHeap.Len() != 0 { for nodeHeap.Len() != 0 {
// Fetch the node within the smallest distance from our source // Fetch the node within the smallest distance from our source
@ -677,7 +672,7 @@ func findPath(g *graphParams, r *RestrictParams,
// If we've reached our source (or we don't have any incoming // If we've reached our source (or we don't have any incoming
// edges), then we're done here and can exit the graph // edges), then we're done here and can exit the graph
// traversal early. // traversal early.
if bytes.Equal(bestNode.PubKeyBytes[:], sourceVertex[:]) { if bestNode.PubKeyBytes == source {
break break
} }
@ -744,7 +739,7 @@ func findPath(g *graphParams, r *RestrictParams,
// If the source node isn't found in the next hop map, then a path // If the source node isn't found in the next hop map, then a path
// doesn't exist, so we terminate in an error. // doesn't exist, so we terminate in an error.
if _, ok := next[sourceVertex]; !ok { if _, ok := next[source]; !ok {
return nil, newErrf(ErrNoPathFound, "unable to find a path to "+ return nil, newErrf(ErrNoPathFound, "unable to find a path to "+
"destination") "destination")
} }
@ -752,8 +747,8 @@ func findPath(g *graphParams, r *RestrictParams,
// Use the nextHop map to unravel the forward path from source to // Use the nextHop map to unravel the forward path from source to
// target. // target.
pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next)) pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next))
currentNode := sourceVertex currentNode := source
for currentNode != targetVertex { // TODO(roasbeef): assumes no cycles for currentNode != target { // TODO(roasbeef): assumes no cycles
// Determine the next hop forward using the next map. // Determine the next hop forward using the next map.
nextNode := next[currentNode] nextNode := next[currentNode]
@ -789,9 +784,10 @@ func findPath(g *graphParams, r *RestrictParams,
// algorithm, rather than attempting to use an unmodified path finding // algorithm, rather than attempting to use an unmodified path finding
// algorithm in a block box manner. // algorithm in a block box manner.
func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph, func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
source *channeldb.LightningNode, target *btcec.PublicKey, source, target Vertex, amt lnwire.MilliSatoshi,
amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32, restrictions *RestrictParams, numPaths uint32,
bandwidthHints map[uint64]lnwire.MilliSatoshi) ([][]*channeldb.ChannelEdgePolicy, error) { bandwidthHints map[uint64]lnwire.MilliSatoshi) (
[][]*channeldb.ChannelEdgePolicy, error) {
// TODO(roasbeef): modifying ordering within heap to eliminate final // TODO(roasbeef): modifying ordering within heap to eliminate final
// sorting step? // sorting step?
@ -821,7 +817,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
// function properly. // function properly.
firstPath := make([]*channeldb.ChannelEdgePolicy, 0, len(startingPath)+1) firstPath := make([]*channeldb.ChannelEdgePolicy, 0, len(startingPath)+1)
firstPath = append(firstPath, &channeldb.ChannelEdgePolicy{ firstPath = append(firstPath, &channeldb.ChannelEdgePolicy{
Node: source, Node: &channeldb.LightningNode{PubKeyBytes: source},
}) })
firstPath = append(firstPath, startingPath...) firstPath = append(firstPath, startingPath...)
@ -908,7 +904,8 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
graph: graph, graph: graph,
bandwidthHints: bandwidthHints, bandwidthHints: bandwidthHints,
}, },
spurRestrictions, spurNode, target, amt, spurRestrictions, spurNode.PubKeyBytes,
target, amt,
) )
// If we weren't able to find a path, we'll continue to // If we weren't able to find a path, we'll continue to

@ -162,7 +162,7 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
return nil, err return nil, err
} }
aliasMap := make(map[string]*btcec.PublicKey) aliasMap := make(map[string]Vertex)
var source *channeldb.LightningNode var source *channeldb.LightningNode
// First we insert all the nodes within the graph as vertexes. // First we insert all the nodes within the graph as vertexes.
@ -189,14 +189,9 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
"must be unique!") "must be unique!")
} }
pub, err := btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil {
return nil, err
}
// If the alias is unique, then add the node to the // If the alias is unique, then add the node to the
// alias map for easy lookup. // alias map for easy lookup.
aliasMap[node.Alias] = pub aliasMap[node.Alias] = dbNode.PubKeyBytes
// If the node is tagged as the source, then we create a // If the node is tagged as the source, then we create a
// pointer to is so we can mark the source in the graph // pointer to is so we can mark the source in the graph
@ -365,7 +360,7 @@ type testGraphInstance struct {
// aliasMap is a map from a node's alias to its public key. This type is // aliasMap is a map from a node's alias to its public key. This type is
// provided in order to allow easily look up from the human memorable alias // provided in order to allow easily look up from the human memorable alias
// to an exact node's public key. // to an exact node's public key.
aliasMap map[string]*btcec.PublicKey aliasMap map[string]Vertex
// privKeyMap maps a node alias to its private key. This is used to be // privKeyMap maps a node alias to its private key. This is used to be
// able to mock a remote node's signing behaviour. // able to mock a remote node's signing behaviour.
@ -394,7 +389,7 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return nil, err return nil, err
} }
aliasMap := make(map[string]*btcec.PublicKey) aliasMap := make(map[string]Vertex)
privKeyMap := make(map[string]*btcec.PrivateKey) privKeyMap := make(map[string]*btcec.PrivateKey)
nodeIndex := byte(0) nodeIndex := byte(0)
@ -429,7 +424,7 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return nil, err return nil, err
} }
aliasMap[alias] = pubKey aliasMap[alias] = dbNode.PubKeyBytes
nodeIndex++ nodeIndex++
return dbNode, nil return dbNode, nil
@ -482,16 +477,13 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
AuthProof: &testAuthProof, AuthProof: &testAuthProof,
ChannelPoint: *fundingPoint, ChannelPoint: *fundingPoint,
Capacity: testChannel.Capacity, Capacity: testChannel.Capacity,
NodeKey1Bytes: aliasMap[testChannel.Node1.Alias],
BitcoinKey1Bytes: aliasMap[testChannel.Node1.Alias],
NodeKey2Bytes: aliasMap[testChannel.Node2.Alias],
BitcoinKey2Bytes: aliasMap[testChannel.Node2.Alias],
} }
node1Bytes := aliasMap[testChannel.Node1.Alias].SerializeCompressed()
node2Bytes := aliasMap[testChannel.Node2.Alias].SerializeCompressed()
copy(edgeInfo.NodeKey1Bytes[:], node1Bytes)
copy(edgeInfo.NodeKey2Bytes[:], node2Bytes)
copy(edgeInfo.BitcoinKey1Bytes[:], node1Bytes)
copy(edgeInfo.BitcoinKey2Bytes[:], node2Bytes)
err = graph.AddChannelEdge(&edgeInfo) err = graph.AddChannelEdge(&edgeInfo)
if err != nil && err != channeldb.ErrEdgeAlreadyExist { if err != nil && err != channeldb.ErrEdgeAlreadyExist {
return nil, err return nil, err
@ -618,7 +610,7 @@ func TestFindLowestFeePath(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, paymentAmt, sourceNode.PubKeyBytes, target, paymentAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
@ -631,20 +623,19 @@ func TestFindLowestFeePath(t *testing.T) {
} }
// Assert that the lowest fee route is returned. // Assert that the lowest fee route is returned.
if !bytes.Equal(route.Hops[1].PubKeyBytes[:], if route.Hops[1].PubKeyBytes != testGraphInstance.aliasMap["b"] {
testGraphInstance.aliasMap["b"].SerializeCompressed()) {
t.Fatalf("expected route to pass through b, "+ t.Fatalf("expected route to pass through b, "+
"but got a route through %v", "but got a route through %v",
getAliasFromPubKey(route.Hops[1].PubKeyBytes[:], getAliasFromPubKey(route.Hops[1].PubKeyBytes,
testGraphInstance.aliasMap)) testGraphInstance.aliasMap))
} }
} }
func getAliasFromPubKey(pubKey []byte, func getAliasFromPubKey(pubKey Vertex,
aliases map[string]*btcec.PublicKey) string { aliases map[string]Vertex) string {
for alias, key := range aliases { for alias, key := range aliases {
if bytes.Equal(key.SerializeCompressed(), pubKey) { if key == pubKey {
return alias return alias
} }
} }
@ -759,7 +750,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
&RestrictParams{ &RestrictParams{
FeeLimit: test.feeLimit, FeeLimit: test.feeLimit,
}, },
sourceNode, target, paymentAmt, sourceNode.PubKeyBytes, target, paymentAmt,
) )
if test.expectFailureNoPath { if test.expectFailureNoPath {
if err == nil { if err == nil {
@ -786,12 +777,11 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
// Check hop nodes // Check hop nodes
for i := 0; i < len(expectedHops); i++ { for i := 0; i < len(expectedHops); i++ {
if !bytes.Equal(route.Hops[i].PubKeyBytes[:], if route.Hops[i].PubKeyBytes != aliases[expectedHops[i].alias] {
aliases[expectedHops[i].alias].SerializeCompressed()) {
t.Fatalf("%v-th hop should be %v, is instead: %v", t.Fatalf("%v-th hop should be %v, is instead: %v",
i, expectedHops[i], i, expectedHops[i],
getAliasFromPubKey(route.Hops[i].PubKeyBytes[:], getAliasFromPubKey(route.Hops[i].PubKeyBytes,
aliases)) aliases))
} }
} }
@ -900,6 +890,8 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
doge := &channeldb.LightningNode{} doge := &channeldb.LightningNode{}
doge.AddPubKey(dogePubKey) doge.AddPubKey(dogePubKey)
doge.Alias = "doge" doge.Alias = "doge"
copy(doge.PubKeyBytes[:], dogePubKeyBytes)
graph.aliasMap["doge"] = doge.PubKeyBytes
// Create the channel edge going from songoku to doge and include it in // Create the channel edge going from songoku to doge and include it in
// our map of additional edges. // our map of additional edges.
@ -912,7 +904,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
} }
additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{ additionalEdges := map[Vertex][]*channeldb.ChannelEdgePolicy{
NewVertex(graph.aliasMap["songoku"]): {songokuToDoge}, graph.aliasMap["songoku"]: {songokuToDoge},
} }
// We should now be able to find a path from roasbeef to doge. // We should now be able to find a path from roasbeef to doge.
@ -924,7 +916,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, dogePubKey, paymentAmt, sourceNode.PubKeyBytes, doge.PubKeyBytes, paymentAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find private path to doge: %v", err) t.Fatalf("unable to find private path to doge: %v", err)
@ -932,7 +924,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
// The path should represent the following hops: // The path should represent the following hops:
// roasbeef -> songoku -> doge // roasbeef -> songoku -> doge
assertExpectedPath(t, path, "songoku", "doge") assertExpectedPath(t, graph.aliasMap, path, "songoku", "doge")
} }
func TestKShortestPathFinding(t *testing.T) { func TestKShortestPathFinding(t *testing.T) {
@ -963,8 +955,8 @@ func TestKShortestPathFinding(t *testing.T) {
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
} }
paths, err := findPaths( paths, err := findPaths(
nil, graph.graph, sourceNode, target, paymentAmt, restrictions, nil, graph.graph, sourceNode.PubKeyBytes, target, paymentAmt,
100, nil, restrictions, 100, nil,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find paths between roasbeef and "+ t.Fatalf("unable to find paths between roasbeef and "+
@ -984,10 +976,12 @@ func TestKShortestPathFinding(t *testing.T) {
} }
// The first route should be a direct route to luo ji. // The first route should be a direct route to luo ji.
assertExpectedPath(t, paths[0], "roasbeef", "luoji") assertExpectedPath(t, graph.aliasMap, paths[0], "roasbeef", "luoji")
// The second route should be a route to luo ji via satoshi. // The second route should be a route to luo ji via satoshi.
assertExpectedPath(t, paths[1], "roasbeef", "satoshi", "luoji") assertExpectedPath(
t, graph.aliasMap, paths[1], "roasbeef", "satoshi", "luoji",
)
} }
// TestNewRoute tests whether the construction of hop payloads by newRoute // TestNewRoute tests whether the construction of hop payloads by newRoute
@ -1235,7 +1229,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, paymentAmt, sourceNode.PubKeyBytes, target, paymentAmt,
) )
if err != nil { if err != nil {
t.Fatalf("path should have been found") t.Fatalf("path should have been found")
@ -1251,7 +1245,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, paymentAmt, sourceNode.PubKeyBytes, target, paymentAmt,
) )
if err == nil { if err == nil {
t.Fatalf("should not have been able to find path, supposed to be "+ t.Fatalf("should not have been able to find path, supposed to be "+
@ -1283,10 +1277,8 @@ func TestPathNotAvailable(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to parse bytes: %v", err) t.Fatalf("unable to parse bytes: %v", err)
} }
unknownNode, err := btcec.ParsePubKey(unknownNodeBytes, btcec.S256()) var unknownNode Vertex
if err != nil { copy(unknownNode[:], unknownNodeBytes)
t.Fatalf("unable to parse pubkey: %v", err)
}
_, err = findPath( _, err = findPath(
&graphParams{ &graphParams{
@ -1295,7 +1287,7 @@ func TestPathNotAvailable(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, unknownNode, 100, sourceNode.PubKeyBytes, unknownNode, 100,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("path shouldn't have been found: %v", err) t.Fatalf("path shouldn't have been found: %v", err)
@ -1334,7 +1326,7 @@ func TestPathInsufficientCapacity(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1369,7 +1361,7 @@ func TestRouteFailMinHTLC(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1429,7 +1421,7 @@ func TestRouteFailMaxHTLC(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("graph should've been able to support payment: %v", err) t.Fatalf("graph should've been able to support payment: %v", err)
@ -1453,7 +1445,7 @@ func TestRouteFailMaxHTLC(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1490,7 +1482,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
@ -1520,7 +1512,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
@ -1547,7 +1539,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1583,12 +1575,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
} }
assertExpectedPath(t, path, "songoku", "sophon") assertExpectedPath(t, graph.aliasMap, path, "songoku", "sophon")
// Now we'll set the bandwidth of the edge roasbeef->songoku and // Now we'll set the bandwidth of the edge roasbeef->songoku and
// roasbeef->phamnuwen to 0. // roasbeef->phamnuwen to 0.
@ -1609,7 +1601,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if !IsError(err, ErrNoPathFound) { if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err) t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1629,12 +1621,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
} }
assertExpectedPath(t, path, "phamnuwen", "sophon") assertExpectedPath(t, graph.aliasMap, path, "phamnuwen", "sophon")
// Finally, set the roasbeef->songoku bandwidth, but also set its // Finally, set the roasbeef->songoku bandwidth, but also set its
// disable flag. // disable flag.
@ -1662,12 +1654,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, payAmt, sourceNode.PubKeyBytes, target, payAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)
} }
assertExpectedPath(t, path, "songoku", "sophon") assertExpectedPath(t, graph.aliasMap, path, "songoku", "sophon")
} }
func TestPathInsufficientCapacityWithFee(t *testing.T) { func TestPathInsufficientCapacityWithFee(t *testing.T) {
@ -1704,7 +1696,11 @@ func TestPathFindSpecExample(t *testing.T) {
// Carol, so we set "B" as the source node so path finding starts from // Carol, so we set "B" as the source node so path finding starts from
// Bob. // Bob.
bob := ctx.aliases["B"] bob := ctx.aliases["B"]
bobNode, err := ctx.graph.FetchLightningNode(bob) bobKey, err := btcec.ParsePubKey(bob[:], btcec.S256())
if err != nil {
t.Fatal(err)
}
bobNode, err := ctx.graph.FetchLightningNode(bobKey)
if err != nil { if err != nil {
t.Fatalf("unable to find bob: %v", err) t.Fatalf("unable to find bob: %v", err)
} }
@ -1715,7 +1711,9 @@ func TestPathFindSpecExample(t *testing.T) {
// Query for a route of 4,999,999 mSAT to carol. // Query for a route of 4,999,999 mSAT to carol.
carol := ctx.aliases["C"] carol := ctx.aliases["C"]
const amt lnwire.MilliSatoshi = 4999999 const amt lnwire.MilliSatoshi = 4999999
routes, err := ctx.router.FindRoutes(carol, amt, noRestrictions, 100) routes, err := ctx.router.FindRoutes(
carol, amt, noRestrictions, 100,
)
if err != nil { if err != nil {
t.Fatalf("unable to find route: %v", err) t.Fatalf("unable to find route: %v", err)
} }
@ -1758,7 +1756,11 @@ func TestPathFindSpecExample(t *testing.T) {
// Next, we'll set A as the source node so we can assert that we create // Next, we'll set A as the source node so we can assert that we create
// the proper route for any queries starting with Alice. // the proper route for any queries starting with Alice.
alice := ctx.aliases["A"] alice := ctx.aliases["A"]
aliceNode, err := ctx.graph.FetchLightningNode(alice) aliceKey, err := btcec.ParsePubKey(alice[:], btcec.S256())
if err != nil {
t.Fatal(err)
}
aliceNode, err := ctx.graph.FetchLightningNode(aliceKey)
if err != nil { if err != nil {
t.Fatalf("unable to find alice: %v", err) t.Fatalf("unable to find alice: %v", err)
} }
@ -1770,13 +1772,15 @@ func TestPathFindSpecExample(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unable to retrieve source node: %v", err) t.Fatalf("unable to retrieve source node: %v", err)
} }
if !bytes.Equal(source.PubKeyBytes[:], alice.SerializeCompressed()) { if source.PubKeyBytes != alice {
t.Fatalf("source node not set") t.Fatalf("source node not set")
} }
// We'll now request a route from A -> B -> C. // We'll now request a route from A -> B -> C.
ctx.router.routeCache = make(map[routeTuple][]*Route) ctx.router.routeCache = make(map[routeTuple][]*Route)
routes, err = ctx.router.FindRoutes(carol, amt, noRestrictions, 100) routes, err = ctx.router.FindRoutes(
carol, amt, noRestrictions, 100,
)
if err != nil { if err != nil {
t.Fatalf("unable to find routes: %v", err) t.Fatalf("unable to find routes: %v", err)
} }
@ -1903,15 +1907,15 @@ func TestPathFindSpecExample(t *testing.T) {
} }
} }
func assertExpectedPath(t *testing.T, path []*channeldb.ChannelEdgePolicy, func assertExpectedPath(t *testing.T, aliasMap map[string]Vertex,
nodeAliases ...string) { path []*channeldb.ChannelEdgePolicy, nodeAliases ...string) {
if len(path) != len(nodeAliases) { if len(path) != len(nodeAliases) {
t.Fatal("number of hops and number of aliases do not match") t.Fatal("number of hops and number of aliases do not match")
} }
for i, hop := range path { for i, hop := range path {
if hop.Node.Alias != nodeAliases[i] { if hop.Node.PubKeyBytes != aliasMap[nodeAliases[i]] {
t.Fatalf("expected %v to be pos #%v in hop, instead "+ t.Fatalf("expected %v to be pos #%v in hop, instead "+
"%v was", nodeAliases[i], i, hop.Node.Alias) "%v was", nodeAliases[i], i, hop.Node.Alias)
} }
@ -1996,7 +2000,7 @@ func TestRestrictOutgoingChannel(t *testing.T) {
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
OutgoingChannelID: &outgoingChannelID, OutgoingChannelID: &outgoingChannelID,
}, },
sourceNode, target, paymentAmt, sourceVertex, target, paymentAmt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)

@ -153,7 +153,8 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
FeeLimit: payment.FeeLimit, FeeLimit: payment.FeeLimit,
OutgoingChannelID: payment.OutgoingChannelID, OutgoingChannelID: payment.OutgoingChannelID,
}, },
p.mc.selfNode, payment.Target, payment.Amount, p.mc.selfNode.PubKeyBytes, payment.Target,
payment.Amount,
) )
if err != nil { if err != nil {
return nil, err return nil, err

@ -1325,9 +1325,9 @@ func pathsToFeeSortedRoutes(source Vertex, paths [][]*channeldb.ChannelEdgePolic
// the required fee and time lock values running backwards along the route. The // the required fee and time lock values running backwards along the route. The
// route that will be ranked the highest is the one with the lowest cumulative // route that will be ranked the highest is the one with the lowest cumulative
// fee along the route. // fee along the route.
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey, func (r *ChannelRouter) FindRoutes(target Vertex, amt lnwire.MilliSatoshi,
amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32, restrictions *RestrictParams, numPaths uint32, finalExpiry ...uint16) (
finalExpiry ...uint16) ([]*Route, error) { []*Route, error) {
var finalCLTVDelta uint16 var finalCLTVDelta uint16
if len(finalExpiry) == 0 { if len(finalExpiry) == 0 {
@ -1336,13 +1336,12 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
finalCLTVDelta = finalExpiry[0] finalCLTVDelta = finalExpiry[0]
} }
dest := target.SerializeCompressed() log.Debugf("Searching for path to %x, sending %v", target, amt)
log.Debugf("Searching for path to %x, sending %v", dest, amt)
// Before attempting to perform a series of graph traversals to find // Before attempting to perform a series of graph traversals to find
// the k-shortest paths to the destination, we'll first consult our // the k-shortest paths to the destination, we'll first consult our
// path cache // path cache
rt := newRouteTuple(amt, dest) rt := newRouteTuple(amt, target[:])
r.routeCacheMtx.RLock() r.routeCacheMtx.RLock()
routes, ok := r.routeCache[rt] routes, ok := r.routeCache[rt]
r.routeCacheMtx.RUnlock() r.routeCacheMtx.RUnlock()
@ -1361,11 +1360,10 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// We can short circuit the routing by opportunistically checking to // We can short circuit the routing by opportunistically checking to
// see if the target vertex event exists in the current graph. // see if the target vertex event exists in the current graph.
targetVertex := NewVertex(target) if _, exists, err := r.cfg.Graph.HasLightningNode(target); err != nil {
if _, exists, err := r.cfg.Graph.HasLightningNode(targetVertex); err != nil {
return nil, err return nil, err
} else if !exists { } else if !exists {
log.Debugf("Target %x is not in known graph", dest) log.Debugf("Target %x is not in known graph", target)
return nil, newErrf(ErrTargetNotInNetwork, "target not found") return nil, newErrf(ErrTargetNotInNetwork, "target not found")
} }
@ -1396,8 +1394,8 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// we'll execute our KSP algorithm to find the k-shortest paths from // we'll execute our KSP algorithm to find the k-shortest paths from
// our source to the destination. // our source to the destination.
shortestPaths, err := findPaths( shortestPaths, err := findPaths(
tx, r.cfg.Graph, r.selfNode, target, amt, restrictions, tx, r.cfg.Graph, r.selfNode.PubKeyBytes, target, amt,
numPaths, bandwidthHints, restrictions, numPaths, bandwidthHints,
) )
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
@ -1421,7 +1419,7 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
} }
go log.Tracef("Obtained %v paths sending %v to %x: %v", len(validRoutes), go log.Tracef("Obtained %v paths sending %v to %x: %v", len(validRoutes),
amt, dest, newLogClosure(func() string { amt, target, newLogClosure(func() string {
return spew.Sdump(validRoutes) return spew.Sdump(validRoutes)
}), }),
) )
@ -1513,7 +1511,7 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte,
// final destination. // final destination.
type LightningPayment struct { type LightningPayment struct {
// Target is the node in which the payment should be routed towards. // Target is the node in which the payment should be routed towards.
Target *btcec.PublicKey Target Vertex
// Amount is the value of the payment to send through the network in // Amount is the value of the payment to send through the network in
// milli-satoshis. // milli-satoshis.
@ -1608,12 +1606,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
log.Tracef("Dispatching route for lightning payment: %v", log.Tracef("Dispatching route for lightning payment: %v",
newLogClosure(func() string { newLogClosure(func() string {
// Remove the public key curve parameters when logging
// the route to prevent spamming the logs.
if payment.Target != nil {
payment.Target.Curve = nil
}
for _, routeHint := range payment.RouteHints { for _, routeHint := range payment.RouteHints {
for _, hopHint := range routeHint { for _, hopHint := range routeHint {
hopHint.NodeID.Curve = nil hopHint.NodeID.Curve = nil

@ -30,7 +30,7 @@ type testCtx struct {
graph *channeldb.ChannelGraph graph *channeldb.ChannelGraph
aliases map[string]*btcec.PublicKey aliases map[string]Vertex
chain *mockChain chain *mockChain
@ -265,11 +265,10 @@ func TestFindRoutesWithFeeLimit(t *testing.T) {
t.Fatalf("expected 2 hops, got %d", len(hops)) t.Fatalf("expected 2 hops, got %d", len(hops))
} }
if !bytes.Equal(hops[0].PubKeyBytes[:], if hops[0].PubKeyBytes != ctx.aliases["songoku"] {
ctx.aliases["songoku"].SerializeCompressed()) {
t.Fatalf("expected first hop through songoku, got %s", t.Fatalf("expected first hop through songoku, got %s",
getAliasFromPubKey(hops[0].PubKeyBytes[:], getAliasFromPubKey(hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -347,12 +346,11 @@ func TestSendPaymentRouteFailureFallback(t *testing.T) {
} }
// The route should have satoshi as the first hop. // The route should have satoshi as the first hop.
if !bytes.Equal(route.Hops[0].PubKeyBytes[:], if route.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] {
ctx.aliases["satoshi"].SerializeCompressed()) {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes[:], getAliasFromPubKey(route.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -410,11 +408,9 @@ func TestChannelUpdateValidation(t *testing.T) {
// Setup a route from source a to destination c. The route will be used // Setup a route from source a to destination c. The route will be used
// in a call to SendToRoute. SendToRoute also applies channel updates, // in a call to SendToRoute. SendToRoute also applies channel updates,
// but it saves us from including RequestRoute in the test scope too. // but it saves us from including RequestRoute in the test scope too.
var hop1 [33]byte hop1 := ctx.aliases["b"]
copy(hop1[:], ctx.aliases["b"].SerializeCompressed())
var hop2 [33]byte hop2 := ctx.aliases["c"]
copy(hop2[:], ctx.aliases["c"].SerializeCompressed())
hops := []*Hop{ hops := []*Hop{
{ {
@ -429,7 +425,7 @@ func TestChannelUpdateValidation(t *testing.T) {
route, err := NewRouteFromHops( route, err := NewRouteFromHops(
lnwire.MilliSatoshi(10000), 100, lnwire.MilliSatoshi(10000), 100,
NewVertex(ctx.aliases["a"]), hops, ctx.aliases["a"], hops,
) )
if err != nil { if err != nil {
t.Fatalf("unable to create route: %v", err) t.Fatalf("unable to create route: %v", err)
@ -451,8 +447,16 @@ func TestChannelUpdateValidation(t *testing.T) {
ctx.router.cfg.SendToSwitch = func(firstHop lnwire.ShortChannelID, ctx.router.cfg.SendToSwitch = func(firstHop lnwire.ShortChannelID,
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
v := ctx.aliases["b"]
source, err := btcec.ParsePubKey(
v[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: ctx.aliases["b"], ErrorSource: source,
FailureMessage: &lnwire.FailFeeInsufficient{ FailureMessage: &lnwire.FailFeeInsufficient{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -576,8 +580,15 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
roasbeefSongoku := lnwire.NewShortChanIDFromInt(chanID) roasbeefSongoku := lnwire.NewShortChanIDFromInt(chanID)
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceNode, ErrorSource: sourceKey,
// Within our error, we'll add a channel update // Within our error, we'll add a channel update
// which is meant to reflect he new fee // which is meant to reflect he new fee
@ -611,12 +622,11 @@ func TestSendPaymentErrorRepeatedFeeInsufficient(t *testing.T) {
} }
// The route should have pham nuwen as the first hop. // The route should have pham nuwen as the first hop.
if !bytes.Equal(route.Hops[0].PubKeyBytes[:], if route.Hops[0].PubKeyBytes != ctx.aliases["phamnuwen"] {
ctx.aliases["phamnuwen"].SerializeCompressed()) {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes[:], getAliasFromPubKey(route.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -684,8 +694,15 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceNode, ErrorSource: sourceKey,
FailureMessage: &lnwire.FailExpiryTooSoon{ FailureMessage: &lnwire.FailExpiryTooSoon{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -712,12 +729,11 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
} }
// The route should have satoshi as the first hop. // The route should have satoshi as the first hop.
if !bytes.Equal(route.Hops[0].PubKeyBytes[:], if route.Hops[0].PubKeyBytes != ctx.aliases["phamnuwen"] {
ctx.aliases["phamnuwen"].SerializeCompressed()) {
t.Fatalf("route should go through phamnuwen as first hop, "+ t.Fatalf("route should go through phamnuwen as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes[:], getAliasFromPubKey(route.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -739,8 +755,15 @@ func TestSendPaymentErrorNonFinalTimeLockErrors(t *testing.T) {
_ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) { _ *lnwire.UpdateAddHTLC, _ *sphinx.Circuit) ([32]byte, error) {
if firstHop == roasbeefSongoku { if firstHop == roasbeefSongoku {
sourceKey, err := btcec.ParsePubKey(
sourceNode[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: sourceNode, ErrorSource: sourceKey,
FailureMessage: &lnwire.FailIncorrectCltvExpiry{ FailureMessage: &lnwire.FailIncorrectCltvExpiry{
Update: errChanUpdate, Update: errChanUpdate,
}, },
@ -823,8 +846,16 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
// prune out the rest of the routes. // prune out the rest of the routes.
roasbeefSatoshi := lnwire.NewShortChanIDFromInt(2340213491) roasbeefSatoshi := lnwire.NewShortChanIDFromInt(2340213491)
if firstHop == roasbeefSatoshi { if firstHop == roasbeefSatoshi {
vertex := ctx.aliases["satoshi"]
key, err := btcec.ParsePubKey(
vertex[:], btcec.S256(),
)
if err != nil {
t.Fatal(err)
}
return [32]byte{}, &htlcswitch.ForwardingError{ return [32]byte{}, &htlcswitch.ForwardingError{
ErrorSource: ctx.aliases["satoshi"], ErrorSource: key,
FailureMessage: &lnwire.FailUnknownNextPeer{}, FailureMessage: &lnwire.FailUnknownNextPeer{},
} }
} }
@ -882,12 +913,11 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
t.Fatalf("incorrect preimage used: expected %x got %x", t.Fatalf("incorrect preimage used: expected %x got %x",
preImage[:], paymentPreImage[:]) preImage[:], paymentPreImage[:])
} }
if !bytes.Equal(route.Hops[0].PubKeyBytes[:], if route.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] {
ctx.aliases["satoshi"].SerializeCompressed()) {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes[:], getAliasFromPubKey(route.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
@ -930,12 +960,11 @@ func TestSendPaymentErrorPathPruning(t *testing.T) {
} }
// The route should have satoshi as the first hop. // The route should have satoshi as the first hop.
if !bytes.Equal(route.Hops[0].PubKeyBytes[:], if route.Hops[0].PubKeyBytes != ctx.aliases["satoshi"] {
ctx.aliases["satoshi"].SerializeCompressed()) {
t.Fatalf("route should go through satoshi as first hop, "+ t.Fatalf("route should go through satoshi as first hop, "+
"instead passes through: %v", "instead passes through: %v",
getAliasFromPubKey(route.Hops[0].PubKeyBytes[:], getAliasFromPubKey(route.Hops[0].PubKeyBytes,
ctx.aliases)) ctx.aliases))
} }
} }
@ -1233,8 +1262,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// We will connect node 1 to "sophon" // We will connect node 1 to "sophon"
connectNode := ctx.aliases["sophon"] connectNode := ctx.aliases["sophon"]
if connectNode == nil { connectNodeKey, err := btcec.ParsePubKey(connectNode[:], btcec.S256())
t.Fatalf("could not find node to connect to") if err != nil {
t.Fatal(err)
} }
var ( var (
@ -1242,12 +1272,12 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
pubKey2 *btcec.PublicKey pubKey2 *btcec.PublicKey
) )
node1Bytes := priv1.PubKey().SerializeCompressed() node1Bytes := priv1.PubKey().SerializeCompressed()
node2Bytes := connectNode.SerializeCompressed() node2Bytes := connectNode
if bytes.Compare(node1Bytes, node2Bytes) == -1 { if bytes.Compare(node1Bytes[:], node2Bytes[:]) == -1 {
pubKey1 = priv1.PubKey() pubKey1 = priv1.PubKey()
pubKey2 = connectNode pubKey2 = connectNodeKey
} else { } else {
pubKey1 = connectNode pubKey1 = connectNodeKey
pubKey2 = priv1.PubKey() pubKey2 = priv1.PubKey()
} }
@ -1267,9 +1297,9 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
AuthProof: nil, AuthProof: nil,
} }
copy(edge.NodeKey1Bytes[:], node1Bytes) copy(edge.NodeKey1Bytes[:], node1Bytes)
copy(edge.NodeKey2Bytes[:], node2Bytes) edge.NodeKey2Bytes = node2Bytes
copy(edge.BitcoinKey1Bytes[:], node1Bytes) copy(edge.BitcoinKey1Bytes[:], node1Bytes)
copy(edge.BitcoinKey2Bytes[:], node2Bytes) edge.BitcoinKey2Bytes = node2Bytes
if err := ctx.router.AddEdge(edge); err != nil { if err := ctx.router.AddEdge(edge); err != nil {
t.Fatalf("unable to add edge to the channel graph: %v.", err) t.Fatalf("unable to add edge to the channel graph: %v.", err)
@ -1308,8 +1338,10 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// We should now be able to find two routes to node 2. // We should now be able to find two routes to node 2.
paymentAmt := lnwire.NewMSatFromSatoshis(100) paymentAmt := lnwire.NewMSatFromSatoshis(100)
targetNode := priv2.PubKey() targetNode := priv2.PubKey()
var targetPubKeyBytes Vertex
copy(targetPubKeyBytes[:], targetNode.SerializeCompressed())
routes, err := ctx.router.FindRoutes( routes, err := ctx.router.FindRoutes(
targetNode, paymentAmt, noRestrictions, defaultNumRoutes, targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
@ -1354,7 +1386,7 @@ func TestAddEdgeUnknownVertexes(t *testing.T) {
// Should still be able to find the routes, and the info should be // Should still be able to find the routes, and the info should be
// updated. // updated.
routes, err = ctx.router.FindRoutes( routes, err = ctx.router.FindRoutes(
targetNode, paymentAmt, noRestrictions, defaultNumRoutes, targetPubKeyBytes, paymentAmt, noRestrictions, defaultNumRoutes,
DefaultFinalCLTVDelta, DefaultFinalCLTVDelta,
) )
if err != nil { if err != nil {
@ -1954,9 +1986,6 @@ func TestFindPathFeeWeighting(t *testing.T) {
amt := lnwire.MilliSatoshi(100) amt := lnwire.MilliSatoshi(100)
target := ctx.aliases["luoji"] target := ctx.aliases["luoji"]
if target == nil {
t.Fatalf("unable to find target node")
}
// We'll now attempt a path finding attempt using this set up. Due to // We'll now attempt a path finding attempt using this set up. Due to
// the edge weighting, we should select the direct path over the 2 hop // the edge weighting, we should select the direct path over the 2 hop
@ -1968,7 +1997,7 @@ func TestFindPathFeeWeighting(t *testing.T) {
&RestrictParams{ &RestrictParams{
FeeLimit: noFeeLimit, FeeLimit: noFeeLimit,
}, },
sourceNode, target, amt, sourceNode.PubKeyBytes, target, amt,
) )
if err != nil { if err != nil {
t.Fatalf("unable to find path: %v", err) t.Fatalf("unable to find path: %v", err)

@ -2764,7 +2764,7 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
type rpcPaymentIntent struct { type rpcPaymentIntent struct {
msat lnwire.MilliSatoshi msat lnwire.MilliSatoshi
feeLimit lnwire.MilliSatoshi feeLimit lnwire.MilliSatoshi
dest *btcec.PublicKey dest routing.Vertex
rHash [32]byte rHash [32]byte
cltvDelta uint16 cltvDelta uint16
routeHints [][]routing.HopHint routeHints [][]routing.HopHint
@ -2778,7 +2778,6 @@ type rpcPaymentIntent struct {
// three ways a client can specify their payment details: a payment request, // three ways a client can specify their payment details: a payment request,
// via manual details, or via a complete route. // via manual details, or via a complete route.
func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) { func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) {
var err error
payIntent := rpcPaymentIntent{} payIntent := rpcPaymentIntent{}
// If a route was specified, then we can use that directly. // If a route was specified, then we can use that directly.
@ -2849,7 +2848,8 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
) )
copy(payIntent.rHash[:], payReq.PaymentHash[:]) copy(payIntent.rHash[:], payReq.PaymentHash[:])
payIntent.dest = payReq.Destination destKey := payReq.Destination.SerializeCompressed()
copy(payIntent.dest[:], destKey)
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry()) payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
payIntent.routeHints = payReq.RouteHints payIntent.routeHints = payReq.RouteHints
@ -2859,24 +2859,20 @@ func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error
// At this point, a destination MUST be specified, so we'll convert it // At this point, a destination MUST be specified, so we'll convert it
// into the proper representation now. The destination will either be // into the proper representation now. The destination will either be
// encoded as raw bytes, or via a hex string. // encoded as raw bytes, or via a hex string.
var pubBytes []byte
if len(rpcPayReq.Dest) != 0 { if len(rpcPayReq.Dest) != 0 {
payIntent.dest, err = btcec.ParsePubKey( pubBytes = rpcPayReq.Dest
rpcPayReq.Dest, btcec.S256(),
)
if err != nil {
return payIntent, err
}
} else { } else {
pubBytes, err := hex.DecodeString(rpcPayReq.DestString) var err error
if err != nil { pubBytes, err = hex.DecodeString(rpcPayReq.DestString)
return payIntent, err
}
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
if err != nil { if err != nil {
return payIntent, err return payIntent, err
} }
} }
if len(pubBytes) != 33 {
return payIntent, errors.New("invalid key length")
}
copy(payIntent.dest[:], pubBytes)
// Otherwise, If the payment request field was not specified // Otherwise, If the payment request field was not specified
// (and a custom route wasn't specified), construct the payment // (and a custom route wasn't specified), construct the payment
@ -4001,17 +3997,19 @@ func (r *rpcServer) GetNodeInfo(ctx context.Context,
func (r *rpcServer) QueryRoutes(ctx context.Context, func (r *rpcServer) QueryRoutes(ctx context.Context,
in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) { in *lnrpc.QueryRoutesRequest) (*lnrpc.QueryRoutesResponse, error) {
// First parse the hex-encoded public key into a full public key object
// we can properly manipulate.
pubKeyBytes, err := hex.DecodeString(in.PubKey) pubKeyBytes, err := hex.DecodeString(in.PubKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
if err != nil { if len(pubKeyBytes) != 33 {
return nil, err return nil, errors.New("invalid key length")
} }
var pubKey routing.Vertex
copy(pubKey[:], pubKeyBytes)
// Currently, within the bootstrap phase of the network, we limit the // Currently, within the bootstrap phase of the network, we limit the
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million // largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
// satoshis. // satoshis.