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

lightningnetwork / lnd / 13157733617

05 Feb 2025 12:49PM UTC coverage: 57.712% (-1.1%) from 58.82%
13157733617

Pull #9447

github

yyforyongyu
sweep: rename methods for clarity

We now rename "third party" to "unknown" as the inputs can be spent via
an older sweeping tx, a third party (anchor), or a remote party (pin).
In fee bumper we don't have the info to distinguish the above cases, and
leave them to be further handled by the sweeper as it has more context.
Pull Request #9447: sweep: start tracking input spending status in the fee bumper

83 of 87 new or added lines in 2 files covered. (95.4%)

19472 existing lines in 252 files now uncovered.

103634 of 179570 relevant lines covered (57.71%)

24840.31 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 {
106✔
236
                lnNode1, _ = vertex1.PubKey()
32✔
237
                lnNode2, _ = vertex2.PubKey()
32✔
238
        } else {
74✔
239
                lnNode1, _ = vertex2.PubKey()
42✔
240
                lnNode2, _ = vertex1.PubKey()
42✔
241
        }
42✔
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