diff --git a/autopilot/graph.go b/autopilot/graph.go index 4fbd0296..15440caf 100644 --- a/autopilot/graph.go +++ b/autopilot/graph.go @@ -270,6 +270,30 @@ func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, nil } +func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) { + nodeKey, err := randKey() + if err != nil { + return nil, err + } + dbNode := &channeldb.LightningNode{ + HaveNodeAnnouncement: true, + Addresses: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, + Features: lnwire.NewFeatureVector(nil, lnwire.GlobalFeatures), + AuthSigBytes: testSig.Serialize(), + } + dbNode.AddPubKey(nodeKey) + if err := d.db.AddLightningNode(dbNode); err != nil { + return nil, err + } + + return nodeKey, nil + +} + // memChannelGraph is an implementation of the autopilot.ChannelGraph backed by // an in-memory graph. type memChannelGraph struct { @@ -335,6 +359,11 @@ func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, if !ok { vertex1 = memNode{ pub: node1, + addrs: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, } } } else { @@ -344,6 +373,11 @@ func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, } vertex1 = memNode{ pub: newPub, + addrs: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, } } @@ -352,6 +386,11 @@ func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, if !ok { vertex2 = memNode{ pub: node2, + addrs: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, } } } else { @@ -361,6 +400,11 @@ func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, } vertex2 = memNode{ pub: newPub, + addrs: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, } } @@ -387,6 +431,24 @@ func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey, return &edge1, &edge2, nil } +func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) { + newPub, err := randKey() + if err != nil { + return nil, err + } + vertex := memNode{ + pub: newPub, + addrs: []net.Addr{ + &net.TCPAddr{ + IP: bytes.Repeat([]byte("a"), 16), + }, + }, + } + m.graph[NewNodeID(newPub)] = vertex + + return newPub, nil +} + // memNode is a purely in-memory implementation of the autopilot.Node // interface. type memNode struct { diff --git a/autopilot/prefattach.go b/autopilot/prefattach.go index d90437b8..65676307 100644 --- a/autopilot/prefattach.go +++ b/autopilot/prefattach.go @@ -148,6 +148,7 @@ func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel, } _, ok := existingPeers[nID] + addrs := addresses[nID] switch { @@ -160,6 +161,16 @@ func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel, // another channel. case chanSize == 0 || chanSize < p.constraints.MinChanSize: continue + + // If the node has no addresses, we cannot connect to it, so we + // skip it for now, which implicitly gives it a score of 0. + case len(addrs) == 0: + continue + + // If the node had no channels, we skip it, since it would have + // gotten a zero score anyway. + case nodeChans == 0: + continue } // Otherwise we score the node according to its fraction of @@ -168,6 +179,7 @@ func (p *ConstrainedPrefAttachment) NodeScores(g ChannelGraph, chans []Channel, candidates[nID] = &AttachmentDirective{ NodeID: nID, ChanAmt: chanSize, + Addrs: addrs, Score: score, } } diff --git a/autopilot/prefattach_test.go b/autopilot/prefattach_test.go index adaee08b..f485ce7d 100644 --- a/autopilot/prefattach_test.go +++ b/autopilot/prefattach_test.go @@ -182,6 +182,8 @@ type testGraph interface { addRandChannel(*btcec.PublicKey, *btcec.PublicKey, btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) + + addRandNode() (*btcec.PublicKey, error) } func newDiskChanGraph() (testGraph, func(), error) { @@ -374,6 +376,12 @@ func TestConstrainedPrefAttachmentSelectTwoVertexes(t *testing.T) { t1.Fatalf("unable to generate channel: %v", err) } + // We also add a third, non-connected node to the graph. + _, err = graph.addRandNode() + if err != nil { + t1.Fatalf("unable to add random node: %v", err) + } + // Get the score for all nodes found in the graph at // this point. nodes := make(map[NodeID]struct{}) @@ -384,7 +392,7 @@ func TestConstrainedPrefAttachmentSelectTwoVertexes(t *testing.T) { t1.Fatalf("unable to traverse graph: %v", err) } - if len(nodes) != 2 { + if len(nodes) != 3 { t1.Fatalf("expected 2 nodes, found %d", len(nodes)) } @@ -399,8 +407,10 @@ func TestConstrainedPrefAttachmentSelectTwoVertexes(t *testing.T) { "directives: %v", err) } - if len(candidates) != len(nodes) { - t1.Fatalf("all nodes should be scored, "+ + // We expect two candidates, since one of the nodes + // doesn't have any channels. + if len(candidates) != 2 { + t1.Fatalf("2 nodes should be scored, "+ "instead %v were", len(candidates)) } @@ -436,6 +446,11 @@ func TestConstrainedPrefAttachmentSelectTwoVertexes(t *testing.T) { "to be %v, instead was %v", expScore, candidate.Score) } + + if len(candidate.Addrs) == 0 { + t1.Fatalf("expected node to have " + + "available addresses, didn't") + } } }) if !success { @@ -633,6 +648,11 @@ func TestConstrainedPrefAttachmentSelectGreedyAllocation(t *testing.T) { "of %v, instead got %v", maxChanSize, candidate.ChanAmt) } + + if len(candidate.Addrs) == 0 { + t1.Fatalf("expected node to have " + + "available addresses, didn't") + } } // Imagine a few channels are being opened, and there's @@ -663,6 +683,11 @@ func TestConstrainedPrefAttachmentSelectGreedyAllocation(t *testing.T) { "of %v, instead got %v", remBalance, candidate.ChanAmt) } + + if len(candidate.Addrs) == 0 { + t1.Fatalf("expected node to have " + + "available addresses, didn't") + } } }) if !success { @@ -753,6 +778,11 @@ func TestConstrainedPrefAttachmentSelectSkipNodes(t *testing.T) { "of %v, instead got %v", maxChanSize, candidate.ChanAmt) } + + if len(candidate.Addrs) == 0 { + t1.Fatalf("expected node to have " + + "available addresses, didn't") + } } // We'll simulate a channel update by adding the nodes