Skip to content

Commit 77549f1

Browse files
authored
Merge pull request lightningnetwork#4384 from bhandras/atpl_bc_topk
add modified greedy topK centrality heuristic to autopilot
2 parents 0fab145 + afbbeae commit 77549f1

6 files changed

+331
-125
lines changed

autopilot/agent.go

+1-12
Original file line numberDiff line numberDiff line change
@@ -657,17 +657,6 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
657657
log.Tracef("Creating attachment directive for chosen node %x",
658658
nID[:])
659659

660-
// Add addresses to the candidates.
661-
addrs := addresses[nID]
662-
663-
// If the node has no known addresses, we cannot connect to it,
664-
// so we'll skip it.
665-
if len(addrs) == 0 {
666-
log.Tracef("Skipping scored node %x with no addresses",
667-
nID[:])
668-
continue
669-
}
670-
671660
// Track the available funds we have left.
672661
if availableFunds < chanSize {
673662
chanSize = availableFunds
@@ -685,7 +674,7 @@ func (a *Agent) openChans(availableFunds btcutil.Amount, numChans uint32,
685674
chanCandidates[nID] = &AttachmentDirective{
686675
NodeID: nID,
687676
ChanAmt: chanSize,
688-
Addrs: addrs,
677+
Addrs: addresses[nID],
689678
}
690679
}
691680

autopilot/betweenness_centrality_test.go

+59-113
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import (
44
"fmt"
55
"testing"
66

7-
"github.com/btcsuite/btcd/btcec"
8-
"github.com/btcsuite/btcutil"
7+
"github.com/stretchr/testify/require"
98
)
109

1110
func TestBetweennessCentralityMetricConstruction(t *testing.T) {
@@ -14,173 +13,120 @@ func TestBetweennessCentralityMetricConstruction(t *testing.T) {
1413

1514
for _, workers := range failing {
1615
m, err := NewBetweennessCentralityMetric(workers)
17-
if m != nil || err == nil {
18-
t.Fatalf("construction must fail with <= 0 workers")
19-
}
16+
require.Error(
17+
t, err, "construction must fail with <= 0 workers",
18+
)
19+
require.Nil(t, m)
2020
}
2121

2222
for _, workers := range ok {
2323
m, err := NewBetweennessCentralityMetric(workers)
24-
if m == nil || err != nil {
25-
t.Fatalf("construction must succeed with >= 1 workers")
26-
}
24+
require.NoError(
25+
t, err, "construction must succeed with >= 1 workers",
26+
)
27+
require.NotNil(t, m)
2728
}
2829
}
2930

3031
// Tests that empty graph results in empty centrality result.
3132
func TestBetweennessCentralityEmptyGraph(t *testing.T) {
3233
centralityMetric, err := NewBetweennessCentralityMetric(1)
33-
if err != nil {
34-
t.Fatalf("construction must succeed with positive number of workers")
35-
}
34+
require.NoError(
35+
t, err,
36+
"construction must succeed with positive number of workers",
37+
)
3638

3739
for _, chanGraph := range chanGraphs {
3840
graph, cleanup, err := chanGraph.genFunc()
3941
success := t.Run(chanGraph.name, func(t1 *testing.T) {
40-
if err != nil {
41-
t1.Fatalf("unable to create graph: %v", err)
42-
}
42+
require.NoError(t, err, "unable to create graph")
43+
4344
if cleanup != nil {
4445
defer cleanup()
4546
}
4647

47-
if err := centralityMetric.Refresh(graph); err != nil {
48-
t.Fatalf("unexpected failure during metric refresh: %v", err)
49-
}
48+
err := centralityMetric.Refresh(graph)
49+
require.NoError(t, err)
5050

5151
centrality := centralityMetric.GetMetric(false)
52-
if len(centrality) > 0 {
53-
t.Fatalf("expected empty metric, got: %v", len(centrality))
54-
}
52+
require.Equal(t, 0, len(centrality))
5553

5654
centrality = centralityMetric.GetMetric(true)
57-
if len(centrality) > 0 {
58-
t.Fatalf("expected empty metric, got: %v", len(centrality))
59-
}
60-
55+
require.Equal(t, 0, len(centrality))
6156
})
6257
if !success {
6358
break
6459
}
6560
}
6661
}
6762

68-
// testGraphDesc is a helper type to describe a test graph.
69-
type testGraphDesc struct {
70-
nodes int
71-
edges map[int][]int
72-
}
73-
74-
// buildTestGraph builds a test graph from a passed graph desriptor.
75-
func buildTestGraph(t *testing.T,
76-
graph testGraph, desc testGraphDesc) map[int]*btcec.PublicKey {
77-
78-
nodes := make(map[int]*btcec.PublicKey)
79-
80-
for i := 0; i < desc.nodes; i++ {
81-
key, err := graph.addRandNode()
82-
if err != nil {
83-
t.Fatalf("cannot create random node")
84-
}
85-
86-
nodes[i] = key
87-
}
88-
89-
const chanCapacity = btcutil.SatoshiPerBitcoin
90-
for u, neighbors := range desc.edges {
91-
for _, v := range neighbors {
92-
_, _, err := graph.addRandChannel(nodes[u], nodes[v], chanCapacity)
93-
if err != nil {
94-
t.Fatalf("unexpected error adding random channel: %v", err)
95-
}
96-
}
97-
}
98-
99-
return nodes
100-
}
101-
10263
// Test betweenness centrality calculating using an example graph.
10364
func TestBetweennessCentralityWithNonEmptyGraph(t *testing.T) {
104-
graphDesc := testGraphDesc{
105-
nodes: 9,
106-
edges: map[int][]int{
107-
0: {1, 2, 3},
108-
1: {2},
109-
2: {3},
110-
3: {4, 5},
111-
4: {5, 6, 7},
112-
5: {6, 7},
113-
6: {7, 8},
114-
},
115-
}
116-
11765
workers := []int{1, 3, 9, 100}
11866

119-
results := []struct {
67+
tests := []struct {
12068
normalize bool
12169
centrality []float64
12270
}{
12371
{
124-
normalize: true,
125-
centrality: []float64{
126-
0.2, 0.0, 0.2, 1.0, 0.4, 0.4, 7.0 / 15.0, 0.0, 0.0,
127-
},
72+
normalize: true,
73+
centrality: normalizedTestGraphCentrality,
12874
},
12975
{
130-
normalize: false,
131-
centrality: []float64{
132-
3.0, 0.0, 3.0, 15.0, 6.0, 6.0, 7.0, 0.0, 0.0,
133-
},
76+
normalize: false,
77+
centrality: testGraphCentrality,
13478
},
13579
}
13680

13781
for _, numWorkers := range workers {
13882
for _, chanGraph := range chanGraphs {
13983
numWorkers := numWorkers
14084
graph, cleanup, err := chanGraph.genFunc()
141-
if err != nil {
142-
t.Fatalf("unable to create graph: %v", err)
143-
}
85+
require.NoError(t, err, "unable to create graph")
86+
14487
if cleanup != nil {
14588
defer cleanup()
14689
}
14790

148-
testName := fmt.Sprintf("%v %d workers", chanGraph.name, numWorkers)
91+
testName := fmt.Sprintf(
92+
"%v %d workers", chanGraph.name, numWorkers,
93+
)
94+
14995
success := t.Run(testName, func(t1 *testing.T) {
150-
centralityMetric, err := NewBetweennessCentralityMetric(
96+
metric, err := NewBetweennessCentralityMetric(
15197
numWorkers,
15298
)
153-
if err != nil {
154-
t.Fatalf("construction must succeed with " +
155-
"positive number of workers")
156-
}
99+
require.NoError(
100+
t, err,
101+
"construction must succeed with "+
102+
"positive number of workers",
103+
)
157104

158-
graphNodes := buildTestGraph(t1, graph, graphDesc)
159-
if err := centralityMetric.Refresh(graph); err != nil {
160-
t1.Fatalf("error while calculating betweeness centrality")
161-
}
162-
for _, expected := range results {
163-
expected := expected
164-
centrality := centralityMetric.GetMetric(expected.normalize)
105+
graphNodes := buildTestGraph(
106+
t1, graph, centralityTestGraph,
107+
)
165108

166-
if len(centrality) != graphDesc.nodes {
167-
t.Fatalf("expected %v values, got: %v",
168-
graphDesc.nodes, len(centrality))
169-
}
109+
err = metric.Refresh(graph)
110+
require.NoError(t, err)
170111

171-
for node, nodeCentrality := range expected.centrality {
172-
nodeID := NewNodeID(graphNodes[node])
173-
calculatedCentrality, ok := centrality[nodeID]
174-
if !ok {
175-
t1.Fatalf("no result for node: %x (%v)",
176-
nodeID, node)
177-
}
178-
179-
if nodeCentrality != calculatedCentrality {
180-
t1.Errorf("centrality for node: %v "+
181-
"should be %v, got: %v",
182-
node, nodeCentrality, calculatedCentrality)
183-
}
112+
for _, expected := range tests {
113+
expected := expected
114+
centrality := metric.GetMetric(
115+
expected.normalize,
116+
)
117+
118+
require.Equal(t,
119+
centralityTestGraph.nodes,
120+
len(centrality),
121+
)
122+
123+
for i, c := range expected.centrality {
124+
nodeID := NewNodeID(
125+
graphNodes[i],
126+
)
127+
result, ok := centrality[nodeID]
128+
require.True(t, ok)
129+
require.Equal(t, c, result)
184130
}
185131
}
186132
})

autopilot/centrality_testdata_test.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package autopilot
2+
3+
import (
4+
"testing"
5+
6+
"github.com/btcsuite/btcd/btcec"
7+
"github.com/btcsuite/btcutil"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
// testGraphDesc is a helper type to describe a test graph.
12+
type testGraphDesc struct {
13+
nodes int
14+
edges map[int][]int
15+
}
16+
17+
var centralityTestGraph = testGraphDesc{
18+
nodes: 9,
19+
edges: map[int][]int{
20+
0: {1, 2, 3},
21+
1: {2},
22+
2: {3},
23+
3: {4, 5},
24+
4: {5, 6, 7},
25+
5: {6, 7},
26+
6: {7, 8},
27+
},
28+
}
29+
30+
var testGraphCentrality = []float64{
31+
3.0, 0.0, 3.0, 15.0, 6.0, 6.0, 7.0, 0.0, 0.0,
32+
}
33+
34+
var normalizedTestGraphCentrality = []float64{
35+
0.2, 0.0, 0.2, 1.0, 0.4, 0.4, 7.0 / 15.0, 0.0, 0.0,
36+
}
37+
38+
// buildTestGraph builds a test graph from a passed graph desriptor.
39+
func buildTestGraph(t *testing.T,
40+
graph testGraph, desc testGraphDesc) map[int]*btcec.PublicKey {
41+
42+
nodes := make(map[int]*btcec.PublicKey)
43+
44+
for i := 0; i < desc.nodes; i++ {
45+
key, err := graph.addRandNode()
46+
require.NoError(t, err, "cannot create random node")
47+
48+
nodes[i] = key
49+
}
50+
51+
const chanCapacity = btcutil.SatoshiPerBitcoin
52+
for u, neighbors := range desc.edges {
53+
for _, v := range neighbors {
54+
_, _, err := graph.addRandChannel(
55+
nodes[u], nodes[v], chanCapacity,
56+
)
57+
require.NoError(t, err,
58+
"unexpected error adding random channel",
59+
)
60+
if err != nil {
61+
t.Fatalf("unexpected error adding"+
62+
"random channel: %v", err)
63+
}
64+
}
65+
}
66+
67+
return nodes
68+
}

autopilot/interface.go

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ var (
185185
availableHeuristics = []AttachmentHeuristic{
186186
NewPrefAttachment(),
187187
NewExternalScoreAttachment(),
188+
NewTopCentrality(),
188189
}
189190

190191
// AvailableHeuristics is a map that holds the name of available

0 commit comments

Comments
 (0)