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

lightningnetwork / lnd / 11216766535

07 Oct 2024 01:37PM UTC coverage: 57.817% (-1.0%) from 58.817%
11216766535

Pull #9148

github

ProofOfKeags
lnwire: remove kickoff feerate from propose/commit
Pull Request #9148: DynComms [2/n]: lnwire: add authenticated wire messages for Dyn*

571 of 879 new or added lines in 16 files covered. (64.96%)

23253 existing lines in 251 files now uncovered.

99022 of 171268 relevant lines covered (57.82%)

38420.67 hits per line

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

68.22
/autopilot/graph.go
1
package autopilot
2

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

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

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

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

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

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

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

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

59
        tx kvdb.RTx
60

61
        node *channeldb.LightningNode
62
}
63

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

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

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

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

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

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

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

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

131
// ForEachNode is a higher-order function that should be called once for each
132
// connected node within the channel graph. If the passed callback returns an
133
// error, then execution should be terminated.
134
//
135
// NOTE: Part of the autopilot.ChannelGraph interface.
136
func (d *databaseChannelGraph) ForEachNode(cb func(Node) error) error {
120✔
137
        return d.db.ForEachNode(func(tx kvdb.RTx, n *channeldb.LightningNode) error {
1,090✔
138
                // We'll skip over any node that doesn't have any advertised
970✔
139
                // addresses. As we won't be able to reach them to actually
970✔
140
                // open any channels.
970✔
141
                if len(n.Addresses) == 0 {
970✔
142
                        return nil
×
143
                }
×
144

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

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

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

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

194
                        return dbNode, nil
141✔
195
                }
196

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

218
                return dbNode, nil
7✔
219
        }
220

221
        vertex1, err := fetchNode(node1)
74✔
222
        if err != nil {
74✔
223
                return nil, nil, err
×
224
        }
×
225

226
        vertex2, err := fetchNode(node2)
74✔
227
        if err != nil {
74✔
228
                return nil, nil, err
×
229
        }
×
230

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

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

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

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

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

322
        return nodeKey, nil
46✔
323

324
}
325

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

332
// A compile time assertion to ensure memChannelGraph meets the
333
// autopilot.ChannelGraph interface.
334
var _ ChannelGraph = (*memChannelGraph)(nil)
335

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

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

356
        return nil
136✔
357
}
358

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

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

372
        return priv.PubKey(), nil
156✔
373
}
374

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

74✔
381
        var (
74✔
382
                vertex1, vertex2 *memNode
74✔
383
                ok               bool
74✔
384
        )
74✔
385

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

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

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

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

74✔
454
        m.graph[NewNodeID(vertex1.pub)] = vertex1
74✔
455
        m.graph[NewNodeID(vertex2.pub)] = vertex2
74✔
456

74✔
457
        return &edge1, &edge2, nil
74✔
458
}
459

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

82✔
475
        return newPub, nil
82✔
476
}
477

478
// databaseChannelGraphCached wraps a channeldb.ChannelGraph instance with the
479
// necessary API to properly implement the autopilot.ChannelGraph interface.
480
type databaseChannelGraphCached struct {
481
        db *channeldb.ChannelGraph
482
}
483

484
// A compile time assertion to ensure databaseChannelGraphCached meets the
485
// autopilot.ChannelGraph interface.
486
var _ ChannelGraph = (*databaseChannelGraphCached)(nil)
487

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

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

504
// A compile time assertion to ensure dbNodeCached meets the autopilot.Node
505
// interface.
506
var _ Node = (*dbNodeCached)(nil)
507

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

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

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

×
540
                if err := cb(edge); err != nil {
×
541
                        return err
×
542
                }
×
543
        }
544

545
        return nil
×
546
}
547

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

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

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

573
        chans []ChannelEdge
574

575
        addrs []net.Addr
576
}
577

578
// A compile time assertion to ensure memNode meets the autopilot.Node
579
// interface.
580
var _ Node = (*memNode)(nil)
581

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

3,956✔
590
        return n
3,956✔
591
}
3,956✔
592

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

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

614
        return nil
965✔
615
}
616

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

623
        num := len(vals)
18✔
624
        switch {
18✔
625
        case num == 0:
3✔
626
                return 0
3✔
627

628
        case num%2 == 0:
8✔
629
                return (vals[num/2-1] + vals[num/2]) / 2
8✔
630

631
        default:
7✔
632
                return vals[num/2]
7✔
633
        }
634
}
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