diff --git a/channeldb/graph.go b/channeldb/graph.go index bea218f9..a7e3399d 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -1075,7 +1075,7 @@ type ChannelAuthProof struct { BitcoinSig1 *btcec.Signature // BitcoinSig2 is the signature using the public key of the second node - // that was used in the channel's mult-sig output. + // that was used in the channel's multi-sig output. BitcoinSig2 *btcec.Signature } diff --git a/fundingmanager.go b/fundingmanager.go index 20017bc5..fe50c608 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1081,8 +1081,8 @@ func newChanAnnouncement(localIdentity, remotePub *btcec.PublicKey, Timestamp: uint32(time.Now().Unix()), Flags: chanFlags, TimeLockDelta: 1, - HtlcMinimumMstat: 0, - FeeBaseMstat: 0, + HtlcMinimumMsat: 0, + FeeBaseMsat: 0, FeeProportionalMillionths: 0, } diff --git a/routing/pathfind.go b/routing/pathfind.go index a2a7e518..41f0c949 100644 --- a/routing/pathfind.go +++ b/routing/pathfind.go @@ -5,6 +5,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/roasbeef/btcd/btcec" + "github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcutil" ) @@ -51,14 +52,33 @@ type Route struct { Hops []*Hop } +// ChannelHop is an intermediate hop within the network with a greater +// multi-hop payment route. This struct contains the relevant routing policy of +// the particular edge, as well as the total capacity, and origin chain of the +// channel itself. +type ChannelHop struct { + // Capacity is the total capacity of the channel being traversed. This + // value is expressed for stability in satoshis. + Capacity btcutil.Amount + + // Chain is a 32-byte has that denotes the base blockchain network of + // the channel. The 32-byte hash is the "genesis" block of the + // blockchain, or the very first block in the chain. + // + // TODO(roasbeef): store chain within edge info/policy in database. + Chain chainhash.Hash + + *channeldb.ChannelEdgePolicy +} + // Hop represents the forwarding details at a particular position within the // final route. This struct houses the values necessary to create the HTLC // which will travel along this hop, and also encode the per-hop payload // included within the Sphinx packet. type Hop struct { - // Channels is the active payment channel that this hop will travel + // Channel is the active payment channel edge that this hop will travel // along. - Channel *channeldb.ChannelEdge + Channel *ChannelHop // TimeLockDelta is the delta that this hop will subtract from the HTLC // before extending it to the next hop in the route. @@ -78,7 +98,7 @@ type Hop struct { // computeFee computes the fee to forward an HTLC of `amt` satoshis over the // passed active payment channel. This value is currently computed as specified // in BOLT07, but will likely change in the near future. -func computeFee(amt btcutil.Amount, edge *channeldb.ChannelEdge) btcutil.Amount { +func computeFee(amt btcutil.Amount, edge *ChannelHop) btcutil.Amount { return edge.FeeBaseMSat + (amt*edge.FeeProportionalMillionths)/1000000 } @@ -94,7 +114,7 @@ func newRoute(amtToSend btcutil.Amount, source, target vertex, // the prevHop map to unravel the path. We end up with a list of edges // in the reverse direction which we'll use to properly calculate the // timelock and fee values. - pathEdges := make([]*channeldb.ChannelEdge, 0, len(prevHop)) + pathEdges := make([]*ChannelHop, 0, len(prevHop)) prev := target for prev != source { // TODO(roasbeef): assumes no cycles // Add the current hop to the limit of path edges then walk @@ -130,7 +150,7 @@ func newRoute(amtToSend btcutil.Amount, source, target vertex, Channel: edge, AmtToForward: runningAmt, Fee: computeFee(runningAmt, edge), - TimeLockDelta: edge.Expiry, + TimeLockDelta: edge.TimeLockDelta, } edge.Node.PubKey.Curve = nil @@ -198,7 +218,7 @@ type nodeWithDist struct { // edgeWithPrev is a helper struct used in path finding that couples an // directional edge with the node's ID in the opposite direction. type edgeWithPrev struct { - edge *channeldb.ChannelEdge + edge *ChannelHop prevNode *btcec.PublicKey } @@ -208,8 +228,8 @@ type edgeWithPrev struct { // should be tuned with experimental and empirical data. // // TODO(roasbeef): compute robust weight metric -func edgeWeight(e *channeldb.ChannelEdge) float64 { - return float64(1 + e.Expiry) +func edgeWeight(e *channeldb.ChannelEdgePolicy) float64 { + return float64(1 + e.TimeLockDelta) } // findRoute attempts to find a path from the source node within the @@ -292,8 +312,9 @@ func findRoute(graph *channeldb.ChannelGraph, target *btcec.PublicKey, } } - // If we've reached our target, then we're done here and can - // exit the graph traversal early. + // If we've reached our target (or we don't have any outgoing + // edges), then we're done here and can exit the graph + // traversal early. if bestNode == nil || bestNode.PubKey.IsEqual(target) { break } @@ -302,7 +323,9 @@ func findRoute(graph *channeldb.ChannelGraph, target *btcec.PublicKey, // examine all the outgoing edge (channels) from this node to // further our graph traversal. pivot := newVertex(bestNode.PubKey) - err := bestNode.ForEachChannel(nil, func(edge *channeldb.ChannelEdge) error { + err := bestNode.ForEachChannel(nil, func(edgeInfo *channeldb.ChannelEdgeInfo, + edge *channeldb.ChannelEdgePolicy) error { + // Compute the tentative distance to this new // channel/edge which is the distance to our current // pivot node plus the weight of this edge. @@ -316,14 +339,15 @@ func findRoute(graph *channeldb.ChannelGraph, target *btcec.PublicKey, // * also add min payment? v := newVertex(edge.Node.PubKey) if tempDist < distance[v].dist { - // TODO(roasbeef): unconditionally add for all - // paths distance[v] = nodeWithDist{ dist: tempDist, node: edge.Node, } prev[v] = edgeWithPrev{ - edge: edge, + edge: &ChannelHop{ + ChannelEdgePolicy: edge, + Capacity: edgeInfo.Capacity, + }, prevNode: bestNode.PubKey, } } diff --git a/routing/pathfind_test.go b/routing/pathfind_test.go index f860e1a1..6c66a7fd 100644 --- a/routing/pathfind_test.go +++ b/routing/pathfind_test.go @@ -5,12 +5,15 @@ import ( "encoding/json" "errors" "io/ioutil" + "math/big" "net" "os" "strings" "testing" "time" + prand "math/rand" + "github.com/lightningnetwork/lnd/channeldb" "github.com/roasbeef/btcd/btcec" "github.com/roasbeef/btcd/chaincfg/chainhash" @@ -22,11 +25,34 @@ const ( // basicGraphFilePath is the file path for a basic graph used within // the tests. The basic graph consists of 5 nodes with 5 channels // connecting them. - basicGraphFilePath = "testdata/basic_graph.json" + basicGraphFilePath = "testdata/basic_graph.json" + + // excessiveHopsGraphFilePath is a file path which stores the JSON dump + // of a graph which was previously triggering an erroneous excessive + // hops error. The error has since been fixed, but a test case + // exercising it is kept around to guard against regressions. excessiveHopsGraphFilePath = "testdata/excessive_hops.json" ) -// testGraph is the struct which coresponds to the JSON format used to encode +var ( + randSource = prand.NewSource(time.Now().Unix()) + randInts = prand.New(randSource) + testSig = &btcec.Signature{ + R: new(big.Int), + S: new(big.Int), + } + _, _ = testSig.R.SetString("63724406601629180062774974542967536251589935445068131219452686511677818569431", 10) + _, _ = testSig.S.SetString("18801056069249825825291287104931333862866033135609736119018462340006816851118", 10) + + testAuthProof = channeldb.ChannelAuthProof{ + NodeSig1: testSig, + NodeSig2: testSig, + BitcoinSig1: testSig, + BitcoinSig2: testSig, + } +) + +// testGraph is the struct which corresponds to the JSON format used to encode // graphs within the files in the testdata directory. // // TODO(roasbeef): add test graph auto-generator @@ -213,20 +239,27 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err // We first insert the existence of the edge between the two // nodes. - if err := graph.AddChannelEdge(node1Pub, node2Pub, &fundingPoint, - edge.ChannelID); err != nil { + edgeInfo := channeldb.ChannelEdgeInfo{ + ChannelID: edge.ChannelID, + NodeKey1: node1Pub, + NodeKey2: node2Pub, + BitcoinKey1: node1Pub, + BitcoinKey2: node2Pub, + AuthProof: &testAuthProof, + ChannelPoint: fundingPoint, + Capacity: btcutil.Amount(edge.Capacity), + } + if err := graph.AddChannelEdge(&edgeInfo); err != nil { return nil, nil, nil, err } - edge := &channeldb.ChannelEdge{ + edgePolicy := &channeldb.ChannelEdgePolicy{ ChannelID: edge.ChannelID, - ChannelPoint: fundingPoint, LastUpdate: time.Now(), - Expiry: edge.Expiry, + TimeLockDelta: edge.Expiry, MinHTLC: btcutil.Amount(edge.MinHTLC), FeeBaseMSat: btcutil.Amount(edge.FeeBaseMsat), FeeProportionalMillionths: btcutil.Amount(edge.FeeRate), - Capacity: btcutil.Amount(edge.Capacity), } // As the graph itself is directed, we need to insert two edges @@ -234,13 +267,13 @@ func parseTestGraph(path string) (*channeldb.ChannelGraph, func(), aliasMap, err // node2->node1. A flag of 0 indicates this is the routing // policy for the first node, and a flag of 1 indicates its the // information for the second node. - edge.Flags = 0 - if err := graph.UpdateEdgeInfo(edge); err != nil { + edgePolicy.Flags = 0 + if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { return nil, nil, nil, err } - edge.Flags = 1 - if err := graph.UpdateEdgeInfo(edge); err != nil { + edgePolicy.Flags = 1 + if err := graph.UpdateEdgePolicy(edgePolicy); err != nil { return nil, nil, nil, err } }