• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 12691323972

09 Jan 2025 01:55PM UTC coverage: 57.495% (-1.1%) from 58.598%
12691323972

Pull #9383

github

ziggie1984
localchans: bugfix so that we always use the correct chanID
Pull Request #9383: bugfix createmissingedge

5 of 7 new or added lines in 1 file covered. (71.43%)

19357 existing lines in 253 files now uncovered.

102541 of 178349 relevant lines covered (57.49%)

24722.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.38
/autopilot/graph.go
1
package autopilot
2

3
import (
4
        "bytes"
5
        "encoding/hex"
6
        "errors"
7
        "net"
8
        "sort"
9
        "sync/atomic"
10
        "time"
11

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
14
        "github.com/btcsuite/btcd/btcutil"
15
        graphdb "github.com/lightningnetwork/lnd/graph/db"
16
        "github.com/lightningnetwork/lnd/graph/db/models"
17
        "github.com/lightningnetwork/lnd/kvdb"
18
        "github.com/lightningnetwork/lnd/lnwire"
19
        "github.com/lightningnetwork/lnd/routing/route"
20
)
21

22
var (
23
        testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7")
24
        testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
25
        testRScalar   = new(btcec.ModNScalar)
26
        testSScalar   = new(btcec.ModNScalar)
27
        _             = testRScalar.SetByteSlice(testRBytes)
28
        _             = testSScalar.SetByteSlice(testSBytes)
29
        testSig       = ecdsa.NewSignature(testRScalar, testSScalar)
30

31
        chanIDCounter uint64 // To be used atomically.
32
)
33

34
// databaseChannelGraph wraps a channeldb.ChannelGraph instance with the
35
// necessary API to properly implement the autopilot.ChannelGraph interface.
36
//
37
// TODO(roasbeef): move inmpl to main package?
38
type databaseChannelGraph struct {
39
        db *graphdb.ChannelGraph
40
}
41

42
// A compile time assertion to ensure databaseChannelGraph meets the
43
// autopilot.ChannelGraph interface.
44
var _ ChannelGraph = (*databaseChannelGraph)(nil)
45

46
// ChannelGraphFromDatabase returns an instance of the autopilot.ChannelGraph
47
// backed by a live, open channeldb instance.
UNCOV
48
func ChannelGraphFromDatabase(db *graphdb.ChannelGraph) ChannelGraph {
×
UNCOV
49
        return &databaseChannelGraph{
×
UNCOV
50
                db: db,
×
UNCOV
51
        }
×
UNCOV
52
}
×
53

54
// type dbNode is a wrapper struct around a database transaction an
55
// channeldb.LightningNode. The wrapper method implement the autopilot.Node
56
// interface.
57
type dbNode struct {
58
        db *graphdb.ChannelGraph
59

60
        tx kvdb.RTx
61

62
        node *models.LightningNode
63
}
64

65
// A compile time assertion to ensure dbNode meets the autopilot.Node
66
// interface.
67
var _ Node = (*dbNode)(nil)
68

69
// PubKey is the identity public key of the node. This will be used to attempt
70
// to target a node for channel opening by the main autopilot agent. The key
71
// will be returned in serialized compressed format.
72
//
73
// NOTE: Part of the autopilot.Node interface.
74
func (d *dbNode) PubKey() [33]byte {
3,874✔
75
        return d.node.PubKeyBytes
3,874✔
76
}
3,874✔
77

78
// Addrs returns a slice of publicly reachable public TCP addresses that the
79
// peer is known to be listening on.
80
//
81
// NOTE: Part of the autopilot.Node interface.
82
func (d *dbNode) Addrs() []net.Addr {
×
83
        return d.node.Addresses
×
84
}
×
85

86
// ForEachChannel is a higher-order function that will be used to iterate
87
// through all edges emanating from/to the target node. For each active
88
// channel, this function should be called with the populated ChannelEdge that
89
// describes the active channel.
90
//
91
// NOTE: Part of the autopilot.Node interface.
92
func (d *dbNode) ForEachChannel(cb func(ChannelEdge) error) error {
965✔
93
        return d.db.ForEachNodeChannelTx(d.tx, d.node.PubKeyBytes,
965✔
94
                func(tx kvdb.RTx, ei *models.ChannelEdgeInfo, ep,
965✔
95
                        _ *models.ChannelEdgePolicy) error {
3,909✔
96

2,944✔
97
                        // Skip channels for which no outgoing edge policy is
2,944✔
98
                        // available.
2,944✔
99
                        //
2,944✔
100
                        // TODO(joostjager): Ideally the case where channels
2,944✔
101
                        // have a nil policy should be supported, as autopilot
2,944✔
102
                        // is not looking at the policies. For now, it is not
2,944✔
103
                        // easily possible to get a reference to the other end
2,944✔
104
                        // LightningNode object without retrieving the policy.
2,944✔
105
                        if ep == nil {
2,944✔
106
                                return nil
×
107
                        }
×
108

109
                        node, err := d.db.FetchLightningNodeTx(
2,944✔
110
                                tx, ep.ToNode,
2,944✔
111
                        )
2,944✔
112
                        if err != nil {
2,944✔
113
                                return err
×
114
                        }
×
115

116
                        edge := ChannelEdge{
2,944✔
117
                                ChanID: lnwire.NewShortChanIDFromInt(
2,944✔
118
                                        ep.ChannelID,
2,944✔
119
                                ),
2,944✔
120
                                Capacity: ei.Capacity,
2,944✔
121
                                Peer: &dbNode{
2,944✔
122
                                        tx:   tx,
2,944✔
123
                                        db:   d.db,
2,944✔
124
                                        node: node,
2,944✔
125
                                },
2,944✔
126
                        }
2,944✔
127

2,944✔
128
                        return cb(edge)
2,944✔
129
                })
130
}
131

132
// ForEachNode is a higher-order function that should be called once for each
133
// connected node within the channel graph. If the passed callback returns an
134
// error, then execution should be terminated.
135
//
136
// NOTE: Part of the autopilot.ChannelGraph interface.
137
func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
120✔
138
        return d.db.ForEachNode(func(tx kvdb.RTx,
120✔
139
                n *models.LightningNode) error {
1,090✔
140

970✔
141
                // We'll skip over any node that doesn't have any advertised
970✔
142
                // addresses. As we won't be able to reach them to actually
970✔
143
                // open any channels.
970✔
144
                if len(n.Addresses) == 0 {
970✔
145
                        return nil
×
146
                }
×
147

148
                node := &dbNode{
970✔
149
                        db:   d.db,
970✔
150
                        tx:   tx,
970✔
151
                        node: n,
970✔
152
                }
970✔
153
                return cb(node)
970✔
154
        })
155
}
156

157
// addRandChannel creates a new channel two target nodes. This function is
158
// meant to aide in the generation of random graphs for use within test cases
159
// the exercise the autopilot package.
160
func (d *databaseChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
161
        capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
74✔
162

74✔
163
        fetchNode := func(pub *btcec.PublicKey) (*models.LightningNode, error) {
222✔
164
                if pub != nil {
289✔
165
                        vertex, err := route.NewVertexFromBytes(
141✔
166
                                pub.SerializeCompressed(),
141✔
167
                        )
141✔
168
                        if err != nil {
141✔
169
                                return nil, err
×
170
                        }
×
171

172
                        dbNode, err := d.db.FetchLightningNode(vertex)
141✔
173
                        switch {
141✔
174
                        case errors.Is(err, graphdb.ErrGraphNodeNotFound):
×
175
                                fallthrough
×
176
                        case errors.Is(err, graphdb.ErrGraphNotFound):
×
177
                                graphNode := &models.LightningNode{
×
178
                                        HaveNodeAnnouncement: true,
×
179
                                        Addresses: []net.Addr{
×
180
                                                &net.TCPAddr{
×
181
                                                        IP: bytes.Repeat([]byte("a"), 16),
×
182
                                                },
×
183
                                        },
×
184
                                        Features: lnwire.NewFeatureVector(
×
185
                                                nil, lnwire.Features,
×
186
                                        ),
×
187
                                        AuthSigBytes: testSig.Serialize(),
×
188
                                }
×
189
                                graphNode.AddPubKey(pub)
×
190
                                if err := d.db.AddLightningNode(graphNode); err != nil {
×
191
                                        return nil, err
×
192
                                }
×
193
                        case err != nil:
×
194
                                return nil, err
×
195
                        }
196

197
                        return dbNode, nil
141✔
198
                }
199

200
                nodeKey, err := randKey()
7✔
201
                if err != nil {
7✔
202
                        return nil, err
×
203
                }
×
204
                dbNode := &models.LightningNode{
7✔
205
                        HaveNodeAnnouncement: true,
7✔
206
                        Addresses: []net.Addr{
7✔
207
                                &net.TCPAddr{
7✔
208
                                        IP: bytes.Repeat([]byte("a"), 16),
7✔
209
                                },
7✔
210
                        },
7✔
211
                        Features: lnwire.NewFeatureVector(
7✔
212
                                nil, lnwire.Features,
7✔
213
                        ),
7✔
214
                        AuthSigBytes: testSig.Serialize(),
7✔
215
                }
7✔
216
                dbNode.AddPubKey(nodeKey)
7✔
217
                if err := d.db.AddLightningNode(dbNode); err != nil {
7✔
218
                        return nil, err
×
219
                }
×
220

221
                return dbNode, nil
7✔
222
        }
223

224
        vertex1, err := fetchNode(node1)
74✔
225
        if err != nil {
74✔
226
                return nil, nil, err
×
227
        }
×
228

229
        vertex2, err := fetchNode(node2)
74✔
230
        if err != nil {
74✔
231
                return nil, nil, err
×
232
        }
×
233

234
        var lnNode1, lnNode2 *btcec.PublicKey
74✔
235
        if bytes.Compare(vertex1.PubKeyBytes[:], vertex2.PubKeyBytes[:]) == -1 {
109✔
236
                lnNode1, _ = vertex1.PubKey()
35✔
237
                lnNode2, _ = vertex2.PubKey()
35✔
238
        } else {
74✔
239
                lnNode1, _ = vertex2.PubKey()
39✔
240
                lnNode2, _ = vertex1.PubKey()
39✔
241
        }
39✔
242

243
        chanID := randChanID()
74✔
244
        edge := &models.ChannelEdgeInfo{
74✔
245
                ChannelID: chanID.ToUint64(),
74✔
246
                Capacity:  capacity,
74✔
247
        }
74✔
248
        edge.AddNodeKeys(lnNode1, lnNode2, lnNode1, lnNode2)
74✔
249
        if err := d.db.AddChannelEdge(edge); err != nil {
74✔
250
                return nil, nil, err
×
251
        }
×
252
        edgePolicy := &models.ChannelEdgePolicy{
74✔
253
                SigBytes:                  testSig.Serialize(),
74✔
254
                ChannelID:                 chanID.ToUint64(),
74✔
255
                LastUpdate:                time.Now(),
74✔
256
                TimeLockDelta:             10,
74✔
257
                MinHTLC:                   1,
74✔
258
                MaxHTLC:                   lnwire.NewMSatFromSatoshis(capacity),
74✔
259
                FeeBaseMSat:               10,
74✔
260
                FeeProportionalMillionths: 10000,
74✔
261
                MessageFlags:              1,
74✔
262
                ChannelFlags:              0,
74✔
263
        }
74✔
264

74✔
265
        if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
74✔
266
                return nil, nil, err
×
267
        }
×
268
        edgePolicy = &models.ChannelEdgePolicy{
74✔
269
                SigBytes:                  testSig.Serialize(),
74✔
270
                ChannelID:                 chanID.ToUint64(),
74✔
271
                LastUpdate:                time.Now(),
74✔
272
                TimeLockDelta:             10,
74✔
273
                MinHTLC:                   1,
74✔
274
                MaxHTLC:                   lnwire.NewMSatFromSatoshis(capacity),
74✔
275
                FeeBaseMSat:               10,
74✔
276
                FeeProportionalMillionths: 10000,
74✔
277
                MessageFlags:              1,
74✔
278
                ChannelFlags:              1,
74✔
279
        }
74✔
280
        if err := d.db.UpdateEdgePolicy(edgePolicy); err != nil {
74✔
281
                return nil, nil, err
×
282
        }
×
283

284
        return &ChannelEdge{
74✔
285
                        ChanID:   chanID,
74✔
286
                        Capacity: capacity,
74✔
287
                        Peer: &dbNode{
74✔
288
                                db:   d.db,
74✔
289
                                node: vertex1,
74✔
290
                        },
74✔
291
                },
74✔
292
                &ChannelEdge{
74✔
293
                        ChanID:   chanID,
74✔
294
                        Capacity: capacity,
74✔
295
                        Peer: &dbNode{
74✔
296
                                db:   d.db,
74✔
297
                                node: vertex2,
74✔
298
                        },
74✔
299
                },
74✔
300
                nil
74✔
301
}
302

303
func (d *databaseChannelGraph) addRandNode() (*btcec.PublicKey, error) {
46✔
304
        nodeKey, err := randKey()
46✔
305
        if err != nil {
46✔
306
                return nil, err
×
307
        }
×
308
        dbNode := &models.LightningNode{
46✔
309
                HaveNodeAnnouncement: true,
46✔
310
                Addresses: []net.Addr{
46✔
311
                        &net.TCPAddr{
46✔
312
                                IP: bytes.Repeat([]byte("a"), 16),
46✔
313
                        },
46✔
314
                },
46✔
315
                Features: lnwire.NewFeatureVector(
46✔
316
                        nil, lnwire.Features,
46✔
317
                ),
46✔
318
                AuthSigBytes: testSig.Serialize(),
46✔
319
        }
46✔
320
        dbNode.AddPubKey(nodeKey)
46✔
321
        if err := d.db.AddLightningNode(dbNode); err != nil {
46✔
322
                return nil, err
×
323
        }
×
324

325
        return nodeKey, nil
46✔
326

327
}
328

329
// memChannelGraph is an implementation of the autopilot.ChannelGraph backed by
330
// an in-memory graph.
331
type memChannelGraph struct {
332
        graph map[NodeID]*memNode
333
}
334

335
// A compile time assertion to ensure memChannelGraph meets the
336
// autopilot.ChannelGraph interface.
337
var _ ChannelGraph = (*memChannelGraph)(nil)
338

339
// newMemChannelGraph creates a new blank in-memory channel graph
340
// implementation.
341
func newMemChannelGraph() *memChannelGraph {
23✔
342
        return &memChannelGraph{
23✔
343
                graph: make(map[NodeID]*memNode),
23✔
344
        }
23✔
345
}
23✔
346

347
// ForEachNode is a higher-order function that should be called once for each
348
// connected node within the channel graph. If the passed callback returns an
349
// error, then execution should be terminated.
350
//
351
// NOTE: Part of the autopilot.ChannelGraph interface.
352
func (m memChannelGraph) ForEachNode(cb func(Node) error) error {
136✔
353
        for _, node := range m.graph {
1,188✔
354
                if err := cb(node); err != nil {
1,052✔
355
                        return err
×
356
                }
×
357
        }
358

359
        return nil
136✔
360
}
361

362
// randChanID generates a new random channel ID.
363
func randChanID() lnwire.ShortChannelID {
225✔
364
        id := atomic.AddUint64(&chanIDCounter, 1)
225✔
365
        return lnwire.NewShortChanIDFromInt(id)
225✔
366
}
225✔
367

368
// randKey returns a random public key.
369
func randKey() (*btcec.PublicKey, error) {
156✔
370
        priv, err := btcec.NewPrivateKey()
156✔
371
        if err != nil {
156✔
372
                return nil, err
×
373
        }
×
374

375
        return priv.PubKey(), nil
156✔
376
}
377

378
// addRandChannel creates a new channel two target nodes. This function is
379
// meant to aide in the generation of random graphs for use within test cases
380
// the exercise the autopilot package.
381
func (m *memChannelGraph) addRandChannel(node1, node2 *btcec.PublicKey,
382
        capacity btcutil.Amount) (*ChannelEdge, *ChannelEdge, error) {
74✔
383

74✔
384
        var (
74✔
385
                vertex1, vertex2 *memNode
74✔
386
                ok               bool
74✔
387
        )
74✔
388

74✔
389
        if node1 != nil {
145✔
390
                vertex1, ok = m.graph[NewNodeID(node1)]
71✔
391
                if !ok {
71✔
392
                        vertex1 = &memNode{
×
393
                                pub: node1,
×
394
                                addrs: []net.Addr{
×
395
                                        &net.TCPAddr{
×
396
                                                IP: bytes.Repeat([]byte("a"), 16),
×
397
                                        },
×
398
                                },
×
399
                        }
×
400
                }
×
401
        } else {
3✔
402
                newPub, err := randKey()
3✔
403
                if err != nil {
3✔
404
                        return nil, nil, err
×
405
                }
×
406
                vertex1 = &memNode{
3✔
407
                        pub: newPub,
3✔
408
                        addrs: []net.Addr{
3✔
409
                                &net.TCPAddr{
3✔
410
                                        IP: bytes.Repeat([]byte("a"), 16),
3✔
411
                                },
3✔
412
                        },
3✔
413
                }
3✔
414
        }
415

416
        if node2 != nil {
144✔
417
                vertex2, ok = m.graph[NewNodeID(node2)]
70✔
418
                if !ok {
70✔
419
                        vertex2 = &memNode{
×
420
                                pub: node2,
×
421
                                addrs: []net.Addr{
×
422
                                        &net.TCPAddr{
×
423
                                                IP: bytes.Repeat([]byte("a"), 16),
×
424
                                        },
×
425
                                },
×
426
                        }
×
427
                }
×
428
        } else {
4✔
429
                newPub, err := randKey()
4✔
430
                if err != nil {
4✔
431
                        return nil, nil, err
×
432
                }
×
433
                vertex2 = &memNode{
4✔
434
                        pub: newPub,
4✔
435
                        addrs: []net.Addr{
4✔
436
                                &net.TCPAddr{
4✔
437
                                        IP: bytes.Repeat([]byte("a"), 16),
4✔
438
                                },
4✔
439
                        },
4✔
440
                }
4✔
441
        }
442

443
        edge1 := ChannelEdge{
74✔
444
                ChanID:   randChanID(),
74✔
445
                Capacity: capacity,
74✔
446
                Peer:     vertex2,
74✔
447
        }
74✔
448
        vertex1.chans = append(vertex1.chans, edge1)
74✔
449

74✔
450
        edge2 := ChannelEdge{
74✔
451
                ChanID:   randChanID(),
74✔
452
                Capacity: capacity,
74✔
453
                Peer:     vertex1,
74✔
454
        }
74✔
455
        vertex2.chans = append(vertex2.chans, edge2)
74✔
456

74✔
457
        m.graph[NewNodeID(vertex1.pub)] = vertex1
74✔
458
        m.graph[NewNodeID(vertex2.pub)] = vertex2
74✔
459

74✔
460
        return &edge1, &edge2, nil
74✔
461
}
462

463
func (m *memChannelGraph) addRandNode() (*btcec.PublicKey, error) {
82✔
464
        newPub, err := randKey()
82✔
465
        if err != nil {
82✔
466
                return nil, err
×
467
        }
×
468
        vertex := &memNode{
82✔
469
                pub: newPub,
82✔
470
                addrs: []net.Addr{
82✔
471
                        &net.TCPAddr{
82✔
472
                                IP: bytes.Repeat([]byte("a"), 16),
82✔
473
                        },
82✔
474
                },
82✔
475
        }
82✔
476
        m.graph[NewNodeID(newPub)] = vertex
82✔
477

82✔
478
        return newPub, nil
82✔
479
}
480

481
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
482
// necessary API to properly implement the autopilot.ChannelGraph interface.
483
type databaseChannelGraphCached struct {
484
        db *graphdb.ChannelGraph
485
}
486

487
// A compile time assertion to ensure databaseChannelGraphCached meets the
488
// autopilot.ChannelGraph interface.
489
var _ ChannelGraph = (*databaseChannelGraphCached)(nil)
490

491
// ChannelGraphFromCachedDatabase returns an instance of the
492
// autopilot.ChannelGraph backed by a live, open channeldb instance.
493
func ChannelGraphFromCachedDatabase(db *graphdb.ChannelGraph) ChannelGraph {
×
494
        return &databaseChannelGraphCached{
×
495
                db: db,
×
496
        }
×
497
}
×
498

499
// dbNodeCached is a wrapper struct around a database transaction for a
500
// channeldb.LightningNode. The wrapper methods implement the autopilot.Node
501
// interface.
502
type dbNodeCached struct {
503
        node     route.Vertex
504
        channels map[uint64]*graphdb.DirectedChannel
505
}
506

507
// A compile time assertion to ensure dbNodeCached meets the autopilot.Node
508
// interface.
509
var _ Node = (*dbNodeCached)(nil)
510

511
// PubKey is the identity public key of the node.
512
//
513
// NOTE: Part of the autopilot.Node interface.
514
func (nc dbNodeCached) PubKey() [33]byte {
×
515
        return nc.node
×
516
}
×
517

518
// Addrs returns a slice of publicly reachable public TCP addresses that the
519
// peer is known to be listening on.
520
//
521
// NOTE: Part of the autopilot.Node interface.
522
func (nc dbNodeCached) Addrs() []net.Addr {
×
523
        // TODO: Add addresses to be usable by autopilot.
×
524
        return []net.Addr{}
×
525
}
×
526

527
// ForEachChannel is a higher-order function that will be used to iterate
528
// through all edges emanating from/to the target node. For each active
529
// channel, this function should be called with the populated ChannelEdge that
530
// describes the active channel.
531
//
532
// NOTE: Part of the autopilot.Node interface.
533
func (nc dbNodeCached) ForEachChannel(cb func(ChannelEdge) error) error {
×
534
        for cid, channel := range nc.channels {
×
535
                edge := ChannelEdge{
×
536
                        ChanID:   lnwire.NewShortChanIDFromInt(cid),
×
537
                        Capacity: channel.Capacity,
×
538
                        Peer: dbNodeCached{
×
539
                                node: channel.OtherNode,
×
540
                        },
×
541
                }
×
542

×
543
                if err := cb(edge); err != nil {
×
544
                        return err
×
545
                }
×
546
        }
547

548
        return nil
×
549
}
550

551
// ForEachNode is a higher-order function that should be called once for each
552
// connected node within the channel graph. If the passed callback returns an
553
// error, then execution should be terminated.
554
//
555
// NOTE: Part of the autopilot.ChannelGraph interface.
556
func (dc *databaseChannelGraphCached) ForEachNode(cb func(Node) error) error {
×
557
        return dc.db.ForEachNodeCached(func(n route.Vertex,
×
558
                channels map[uint64]*graphdb.DirectedChannel) error {
×
559

×
560
                if len(channels) > 0 {
×
561
                        node := dbNodeCached{
×
562
                                node:     n,
×
563
                                channels: channels,
×
564
                        }
×
565
                        return cb(node)
×
566
                }
×
567
                return nil
×
568
        })
569
}
570

571
// memNode is a purely in-memory implementation of the autopilot.Node
572
// interface.
573
type memNode struct {
574
        pub *btcec.PublicKey
575

576
        chans []ChannelEdge
577

578
        addrs []net.Addr
579
}
580

581
// A compile time assertion to ensure memNode meets the autopilot.Node
582
// interface.
583
var _ Node = (*memNode)(nil)
584

585
// PubKey is the identity public key of the node. This will be used to attempt
586
// to target a node for channel opening by the main autopilot agent.
587
//
588
// NOTE: Part of the autopilot.Node interface.
589
func (m memNode) PubKey() [33]byte {
3,956✔
590
        var n [33]byte
3,956✔
591
        copy(n[:], m.pub.SerializeCompressed())
3,956✔
592

3,956✔
593
        return n
3,956✔
594
}
3,956✔
595

596
// Addrs returns a slice of publicly reachable public TCP addresses that the
597
// peer is known to be listening on.
598
//
599
// NOTE: Part of the autopilot.Node interface.
600
func (m memNode) Addrs() []net.Addr {
82✔
601
        return m.addrs
82✔
602
}
82✔
603

604
// ForEachChannel is a higher-order function that will be used to iterate
605
// through all edges emanating from/to the target node. For each active
606
// channel, this function should be called with the populated ChannelEdge that
607
// describes the active channel.
608
//
609
// NOTE: Part of the autopilot.Node interface.
610
func (m memNode) ForEachChannel(cb func(ChannelEdge) error) error {
965✔
611
        for _, channel := range m.chans {
3,909✔
612
                if err := cb(channel); err != nil {
2,944✔
613
                        return err
×
614
                }
×
615
        }
616

617
        return nil
965✔
618
}
619

620
// Median returns the median value in the slice of Amounts.
621
func Median(vals []btcutil.Amount) btcutil.Amount {
18✔
622
        sort.Slice(vals, func(i, j int) bool {
39✔
623
                return vals[i] < vals[j]
21✔
624
        })
21✔
625

626
        num := len(vals)
18✔
627
        switch {
18✔
628
        case num == 0:
3✔
629
                return 0
3✔
630

631
        case num%2 == 0:
8✔
632
                return (vals[num/2-1] + vals[num/2]) / 2
8✔
633

634
        default:
7✔
635
                return vals[num/2]
7✔
636
        }
637
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc