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
// payment's destination.
func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
target *btcec.PublicKey) (*paymentSession, error) {
target Vertex) (*paymentSession, error) {
viewSnapshot := m.GraphPruneView()
@ -175,7 +175,13 @@ func (m *missionControl) NewPaymentSession(routeHints [][]HopHint,
if i != len(routeHint)-1 {
endNode.AddPubKey(routeHint[i+1].NodeID)
} 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

@ -1,7 +1,6 @@
package routing
import (
"bytes"
"encoding/binary"
"fmt"
"math"
@ -429,8 +428,7 @@ type RestrictParams struct {
// destination node back to source. This is to properly accumulate fees
// that need to be paid along the path and accurately check the amount
// to forward at every node against the available bandwidth.
func findPath(g *graphParams, r *RestrictParams,
sourceNode *channeldb.LightningNode, target *btcec.PublicKey,
func findPath(g *graphParams, r *RestrictParams, source, target Vertex,
amt lnwire.MilliSatoshi) ([]*channeldb.ChannelEdgePolicy, 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
// advertised to the network and included in the graph.ForEachNode call
// 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
// point of the graph traversal. We are searching backwards to get the
// fees first time right and correctly match channel bandwidth.
targetVertex := NewVertex(target)
targetNode := &channeldb.LightningNode{PubKeyBytes: targetVertex}
distance[targetVertex] = nodeWithDist{
targetNode := &channeldb.LightningNode{PubKeyBytes: target}
distance[target] = nodeWithDist{
dist: 0,
node: targetNode,
amountToReceive: amt,
@ -534,7 +529,7 @@ func findPath(g *graphParams, r *RestrictParams,
// skip it.
// TODO(halseth): also ignore disable flags for non-local
// channels if bandwidth hint is set?
isSourceChan := fromVertex == sourceVertex
isSourceChan := fromVertex == source
edgeFlags := edge.ChannelFlags
isDisabled := edgeFlags&lnwire.ChanUpdateDisabled != 0
@ -597,7 +592,7 @@ func findPath(g *graphParams, r *RestrictParams,
// node, no additional timelock is required.
var fee lnwire.MilliSatoshi
var timeLockDelta uint16
if fromVertex != sourceVertex {
if fromVertex != source {
fee = computeFee(amountToSend, edge)
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
// heap.
heap.Push(&nodeHeap, distance[targetVertex])
heap.Push(&nodeHeap, distance[target])
for nodeHeap.Len() != 0 {
// 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
// edges), then we're done here and can exit the graph
// traversal early.
if bytes.Equal(bestNode.PubKeyBytes[:], sourceVertex[:]) {
if bestNode.PubKeyBytes == source {
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
// 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 "+
"destination")
}
@ -752,8 +747,8 @@ func findPath(g *graphParams, r *RestrictParams,
// Use the nextHop map to unravel the forward path from source to
// target.
pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next))
currentNode := sourceVertex
for currentNode != targetVertex { // TODO(roasbeef): assumes no cycles
currentNode := source
for currentNode != target { // TODO(roasbeef): assumes no cycles
// Determine the next hop forward using the next map.
nextNode := next[currentNode]
@ -789,9 +784,10 @@ func findPath(g *graphParams, r *RestrictParams,
// algorithm, rather than attempting to use an unmodified path finding
// algorithm in a block box manner.
func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
source *channeldb.LightningNode, target *btcec.PublicKey,
amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32,
bandwidthHints map[uint64]lnwire.MilliSatoshi) ([][]*channeldb.ChannelEdgePolicy, error) {
source, target Vertex, amt lnwire.MilliSatoshi,
restrictions *RestrictParams, numPaths uint32,
bandwidthHints map[uint64]lnwire.MilliSatoshi) (
[][]*channeldb.ChannelEdgePolicy, error) {
// TODO(roasbeef): modifying ordering within heap to eliminate final
// sorting step?
@ -821,7 +817,7 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
// function properly.
firstPath := make([]*channeldb.ChannelEdgePolicy, 0, len(startingPath)+1)
firstPath = append(firstPath, &channeldb.ChannelEdgePolicy{
Node: source,
Node: &channeldb.LightningNode{PubKeyBytes: source},
})
firstPath = append(firstPath, startingPath...)
@ -908,7 +904,8 @@ func findPaths(tx *bbolt.Tx, graph *channeldb.ChannelGraph,
graph: graph,
bandwidthHints: bandwidthHints,
},
spurRestrictions, spurNode, target, amt,
spurRestrictions, spurNode.PubKeyBytes,
target, amt,
)
// 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
}
aliasMap := make(map[string]*btcec.PublicKey)
aliasMap := make(map[string]Vertex)
var source *channeldb.LightningNode
// First we insert all the nodes within the graph as vertexes.
@ -189,14 +189,9 @@ func parseTestGraph(path string) (*testGraphInstance, error) {
"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
// 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
// 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
// provided in order to allow easily look up from the human memorable alias
// 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
// able to mock a remote node's signing behaviour.
@ -394,7 +389,7 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return nil, err
}
aliasMap := make(map[string]*btcec.PublicKey)
aliasMap := make(map[string]Vertex)
privKeyMap := make(map[string]*btcec.PrivateKey)
nodeIndex := byte(0)
@ -429,7 +424,7 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
return nil, err
}
aliasMap[alias] = pubKey
aliasMap[alias] = dbNode.PubKeyBytes
nodeIndex++
return dbNode, nil
@ -482,16 +477,13 @@ func createTestGraphFromChannels(testChannels []*testChannel) (*testGraphInstanc
AuthProof: &testAuthProof,
ChannelPoint: *fundingPoint,
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)
if err != nil && err != channeldb.ErrEdgeAlreadyExist {
return nil, err
@ -618,7 +610,7 @@ func TestFindLowestFeePath(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, paymentAmt,
sourceNode.PubKeyBytes, target, paymentAmt,
)
if err != nil {
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.
if !bytes.Equal(route.Hops[1].PubKeyBytes[:],
testGraphInstance.aliasMap["b"].SerializeCompressed()) {
if route.Hops[1].PubKeyBytes != testGraphInstance.aliasMap["b"] {
t.Fatalf("expected route to pass through b, "+
"but got a route through %v",
getAliasFromPubKey(route.Hops[1].PubKeyBytes[:],
getAliasFromPubKey(route.Hops[1].PubKeyBytes,
testGraphInstance.aliasMap))
}
}
func getAliasFromPubKey(pubKey []byte,
aliases map[string]*btcec.PublicKey) string {
func getAliasFromPubKey(pubKey Vertex,
aliases map[string]Vertex) string {
for alias, key := range aliases {
if bytes.Equal(key.SerializeCompressed(), pubKey) {
if key == pubKey {
return alias
}
}
@ -759,7 +750,7 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
&RestrictParams{
FeeLimit: test.feeLimit,
},
sourceNode, target, paymentAmt,
sourceNode.PubKeyBytes, target, paymentAmt,
)
if test.expectFailureNoPath {
if err == nil {
@ -786,12 +777,11 @@ func testBasicGraphPathFindingCase(t *testing.T, graphInstance *testGraphInstanc
// Check hop nodes
for i := 0; i < len(expectedHops); i++ {
if !bytes.Equal(route.Hops[i].PubKeyBytes[:],
aliases[expectedHops[i].alias].SerializeCompressed()) {
if route.Hops[i].PubKeyBytes != aliases[expectedHops[i].alias] {
t.Fatalf("%v-th hop should be %v, is instead: %v",
i, expectedHops[i],
getAliasFromPubKey(route.Hops[i].PubKeyBytes[:],
getAliasFromPubKey(route.Hops[i].PubKeyBytes,
aliases))
}
}
@ -900,6 +890,8 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
doge := &channeldb.LightningNode{}
doge.AddPubKey(dogePubKey)
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
// our map of additional edges.
@ -912,7 +904,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
}
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.
@ -924,7 +916,7 @@ func TestPathFindingWithAdditionalEdges(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, dogePubKey, paymentAmt,
sourceNode.PubKeyBytes, doge.PubKeyBytes, paymentAmt,
)
if err != nil {
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:
// roasbeef -> songoku -> doge
assertExpectedPath(t, path, "songoku", "doge")
assertExpectedPath(t, graph.aliasMap, path, "songoku", "doge")
}
func TestKShortestPathFinding(t *testing.T) {
@ -963,8 +955,8 @@ func TestKShortestPathFinding(t *testing.T) {
FeeLimit: noFeeLimit,
}
paths, err := findPaths(
nil, graph.graph, sourceNode, target, paymentAmt, restrictions,
100, nil,
nil, graph.graph, sourceNode.PubKeyBytes, target, paymentAmt,
restrictions, 100, nil,
)
if err != nil {
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.
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.
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
@ -1235,7 +1229,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, paymentAmt,
sourceNode.PubKeyBytes, target, paymentAmt,
)
if err != nil {
t.Fatalf("path should have been found")
@ -1251,7 +1245,7 @@ func TestNewRoutePathTooLong(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, paymentAmt,
sourceNode.PubKeyBytes, target, paymentAmt,
)
if err == nil {
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 {
t.Fatalf("unable to parse bytes: %v", err)
}
unknownNode, err := btcec.ParsePubKey(unknownNodeBytes, btcec.S256())
if err != nil {
t.Fatalf("unable to parse pubkey: %v", err)
}
var unknownNode Vertex
copy(unknownNode[:], unknownNodeBytes)
_, err = findPath(
&graphParams{
@ -1295,7 +1287,7 @@ func TestPathNotAvailable(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, unknownNode, 100,
sourceNode.PubKeyBytes, unknownNode, 100,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("path shouldn't have been found: %v", err)
@ -1334,7 +1326,7 @@ func TestPathInsufficientCapacity(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1369,7 +1361,7 @@ func TestRouteFailMinHTLC(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1429,7 +1421,7 @@ func TestRouteFailMaxHTLC(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
t.Fatalf("graph should've been able to support payment: %v", err)
@ -1453,7 +1445,7 @@ func TestRouteFailMaxHTLC(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1490,7 +1482,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)
@ -1520,7 +1512,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)
@ -1547,7 +1539,7 @@ func TestRouteFailDisabledEdge(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1583,12 +1575,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
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
// roasbeef->phamnuwen to 0.
@ -1609,7 +1601,7 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if !IsError(err, ErrNoPathFound) {
t.Fatalf("graph shouldn't be able to support payment: %v", err)
@ -1629,12 +1621,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
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
// disable flag.
@ -1662,12 +1654,12 @@ func TestPathSourceEdgesBandwidth(t *testing.T) {
&RestrictParams{
FeeLimit: noFeeLimit,
},
sourceNode, target, payAmt,
sourceNode.PubKeyBytes, target, payAmt,
)
if err != nil {
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) {
@ -1704,7 +1696,11 @@ func TestPathFindSpecExample(t *testing.T) {
// Carol, so we set "B" as the source node so path finding starts from
// Bob.
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 {
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.
carol := ctx.aliases["C"]
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 {
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
// the proper route for any queries starting with Alice.
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 {
t.Fatalf("unable to find alice: %v", err)
}
@ -1770,13 +1772,15 @@ func TestPathFindSpecExample(t *testing.T) {
if err != nil {
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")
}
// We'll now request a route from A -> B -> C.
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 {
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,
nodeAliases ...string) {
func assertExpectedPath(t *testing.T, aliasMap map[string]Vertex,
path []*channeldb.ChannelEdgePolicy, nodeAliases ...string) {
if len(path) != len(nodeAliases) {
t.Fatal("number of hops and number of aliases do not match")
}
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 "+
"%v was", nodeAliases[i], i, hop.Node.Alias)
}
@ -1996,7 +2000,7 @@ func TestRestrictOutgoingChannel(t *testing.T) {
FeeLimit: noFeeLimit,
OutgoingChannelID: &outgoingChannelID,
},
sourceNode, target, paymentAmt,
sourceVertex, target, paymentAmt,
)
if err != nil {
t.Fatalf("unable to find path: %v", err)

@ -153,7 +153,8 @@ func (p *paymentSession) RequestRoute(payment *LightningPayment,
FeeLimit: payment.FeeLimit,
OutgoingChannelID: payment.OutgoingChannelID,
},
p.mc.selfNode, payment.Target, payment.Amount,
p.mc.selfNode.PubKeyBytes, payment.Target,
payment.Amount,
)
if err != nil {
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
// route that will be ranked the highest is the one with the lowest cumulative
// fee along the route.
func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
amt lnwire.MilliSatoshi, restrictions *RestrictParams, numPaths uint32,
finalExpiry ...uint16) ([]*Route, error) {
func (r *ChannelRouter) FindRoutes(target Vertex, amt lnwire.MilliSatoshi,
restrictions *RestrictParams, numPaths uint32, finalExpiry ...uint16) (
[]*Route, error) {
var finalCLTVDelta uint16
if len(finalExpiry) == 0 {
@ -1336,13 +1336,12 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
finalCLTVDelta = finalExpiry[0]
}
dest := target.SerializeCompressed()
log.Debugf("Searching for path to %x, sending %v", dest, amt)
log.Debugf("Searching for path to %x, sending %v", target, amt)
// Before attempting to perform a series of graph traversals to find
// the k-shortest paths to the destination, we'll first consult our
// path cache
rt := newRouteTuple(amt, dest)
rt := newRouteTuple(amt, target[:])
r.routeCacheMtx.RLock()
routes, ok := r.routeCache[rt]
r.routeCacheMtx.RUnlock()
@ -1361,11 +1360,10 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// We can short circuit the routing by opportunistically checking to
// see if the target vertex event exists in the current graph.
targetVertex := NewVertex(target)
if _, exists, err := r.cfg.Graph.HasLightningNode(targetVertex); err != nil {
if _, exists, err := r.cfg.Graph.HasLightningNode(target); err != nil {
return nil, err
} 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")
}
@ -1396,8 +1394,8 @@ func (r *ChannelRouter) FindRoutes(target *btcec.PublicKey,
// we'll execute our KSP algorithm to find the k-shortest paths from
// our source to the destination.
shortestPaths, err := findPaths(
tx, r.cfg.Graph, r.selfNode, target, amt, restrictions,
numPaths, bandwidthHints,
tx, r.cfg.Graph, r.selfNode.PubKeyBytes, target, amt,
restrictions, numPaths, bandwidthHints,
)
if err != nil {
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),
amt, dest, newLogClosure(func() string {
amt, target, newLogClosure(func() string {
return spew.Sdump(validRoutes)
}),
)
@ -1513,7 +1511,7 @@ func generateSphinxPacket(route *Route, paymentHash []byte) ([]byte,
// final destination.
type LightningPayment struct {
// 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
// milli-satoshis.
@ -1608,12 +1606,6 @@ func (r *ChannelRouter) sendPayment(payment *LightningPayment,
log.Tracef("Dispatching route for lightning payment: %v",
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 _, hopHint := range routeHint {
hopHint.NodeID.Curve = nil

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

@ -2764,7 +2764,7 @@ func unmarshallSendToRouteRequest(req *lnrpc.SendToRouteRequest,
type rpcPaymentIntent struct {
msat lnwire.MilliSatoshi
feeLimit lnwire.MilliSatoshi
dest *btcec.PublicKey
dest routing.Vertex
rHash [32]byte
cltvDelta uint16
routeHints [][]routing.HopHint
@ -2778,7 +2778,6 @@ type rpcPaymentIntent struct {
// three ways a client can specify their payment details: a payment request,
// via manual details, or via a complete route.
func extractPaymentIntent(rpcPayReq *rpcPaymentRequest) (rpcPaymentIntent, error) {
var err error
payIntent := rpcPaymentIntent{}
// 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[:])
payIntent.dest = payReq.Destination
destKey := payReq.Destination.SerializeCompressed()
copy(payIntent.dest[:], destKey)
payIntent.cltvDelta = uint16(payReq.MinFinalCLTVExpiry())
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
// into the proper representation now. The destination will either be
// encoded as raw bytes, or via a hex string.
var pubBytes []byte
if len(rpcPayReq.Dest) != 0 {
payIntent.dest, err = btcec.ParsePubKey(
rpcPayReq.Dest, btcec.S256(),
)
if err != nil {
return payIntent, err
}
pubBytes = rpcPayReq.Dest
} else {
pubBytes, err := hex.DecodeString(rpcPayReq.DestString)
if err != nil {
return payIntent, err
}
payIntent.dest, err = btcec.ParsePubKey(pubBytes, btcec.S256())
var err error
pubBytes, err = hex.DecodeString(rpcPayReq.DestString)
if err != nil {
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
// (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,
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)
if err != nil {
return nil, err
}
pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256())
if err != nil {
return nil, err
if len(pubKeyBytes) != 33 {
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
// largest payment size allotted to (2^32) - 1 mSAT or 4.29 million
// satoshis.