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

lightningnetwork / lnd / 16673052227

01 Aug 2025 10:44AM UTC coverage: 67.016% (-0.03%) from 67.047%
16673052227

Pull #9888

github

web-flow
Merge 1dd8765d7 into 37523b6cb
Pull Request #9888: Attributable failures

325 of 384 new or added lines in 16 files covered. (84.64%)

131 existing lines in 24 files now uncovered.

135611 of 202355 relevant lines covered (67.02%)

21613.83 hits per line

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

74.17
/htlcswitch/mock.go
1
package htlcswitch
2

3
import (
4
        "bytes"
5
        "context"
6
        "crypto/sha256"
7
        "encoding/binary"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "net"
12
        "path/filepath"
13
        "sync"
14
        "sync/atomic"
15
        "testing"
16
        "time"
17

18
        "github.com/btcsuite/btcd/btcec/v2"
19
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
20
        "github.com/btcsuite/btcd/btcutil"
21
        "github.com/btcsuite/btcd/wire"
22
        sphinx "github.com/lightningnetwork/lightning-onion"
23
        "github.com/lightningnetwork/lnd/chainntnfs"
24
        "github.com/lightningnetwork/lnd/channeldb"
25
        "github.com/lightningnetwork/lnd/clock"
26
        "github.com/lightningnetwork/lnd/contractcourt"
27
        "github.com/lightningnetwork/lnd/fn/v2"
28
        "github.com/lightningnetwork/lnd/graph/db/models"
29
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
30
        "github.com/lightningnetwork/lnd/invoices"
31
        "github.com/lightningnetwork/lnd/lnpeer"
32
        "github.com/lightningnetwork/lnd/lntest/mock"
33
        "github.com/lightningnetwork/lnd/lntypes"
34
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
35
        "github.com/lightningnetwork/lnd/lnwire"
36
        "github.com/lightningnetwork/lnd/ticker"
37
        "github.com/lightningnetwork/lnd/tlv"
38
)
39

40
func isAlias(scid lnwire.ShortChannelID) bool {
109✔
41
        return scid.BlockHeight >= 16_000_000 && scid.BlockHeight < 16_250_000
109✔
42
}
109✔
43

44
type mockPreimageCache struct {
45
        sync.Mutex
46
        preimageMap map[lntypes.Hash]lntypes.Preimage
47
}
48

49
func newMockPreimageCache() *mockPreimageCache {
273✔
50
        return &mockPreimageCache{
273✔
51
                preimageMap: make(map[lntypes.Hash]lntypes.Preimage),
273✔
52
        }
273✔
53
}
273✔
54

55
func (m *mockPreimageCache) LookupPreimage(
56
        hash lntypes.Hash) (lntypes.Preimage, bool) {
80✔
57

80✔
58
        m.Lock()
80✔
59
        defer m.Unlock()
80✔
60

80✔
61
        p, ok := m.preimageMap[hash]
80✔
62
        return p, ok
80✔
63
}
80✔
64

65
func (m *mockPreimageCache) AddPreimages(preimages ...lntypes.Preimage) error {
1,368✔
66
        m.Lock()
1,368✔
67
        defer m.Unlock()
1,368✔
68

1,368✔
69
        for _, preimage := range preimages {
1,593✔
70
                m.preimageMap[preimage.Hash()] = preimage
225✔
71
        }
225✔
72

73
        return nil
1,368✔
74
}
75

76
func (m *mockPreimageCache) SubscribeUpdates(
77
        chanID lnwire.ShortChannelID, htlc *channeldb.HTLC,
78
        payload *hop.Payload,
79
        nextHopOnionBlob []byte) (*contractcourt.WitnessSubscription, error) {
×
80

×
81
        return nil, nil
×
82
}
×
83

84
// TODO(yy): replace it with chainfee.MockEstimator.
85
type mockFeeEstimator struct {
86
        byteFeeIn chan chainfee.SatPerKWeight
87
        relayFee  chan chainfee.SatPerKWeight
88

89
        quit chan struct{}
90
}
91

92
func newMockFeeEstimator() *mockFeeEstimator {
92✔
93
        return &mockFeeEstimator{
92✔
94
                byteFeeIn: make(chan chainfee.SatPerKWeight),
92✔
95
                relayFee:  make(chan chainfee.SatPerKWeight),
92✔
96
                quit:      make(chan struct{}),
92✔
97
        }
92✔
98
}
92✔
99

100
func (m *mockFeeEstimator) EstimateFeePerKW(
101
        numBlocks uint32) (chainfee.SatPerKWeight, error) {
4✔
102

4✔
103
        select {
4✔
104
        case feeRate := <-m.byteFeeIn:
4✔
105
                return feeRate, nil
4✔
106
        case <-m.quit:
×
107
                return 0, fmt.Errorf("exiting")
×
108
        }
109
}
110

111
func (m *mockFeeEstimator) RelayFeePerKW() chainfee.SatPerKWeight {
4✔
112
        select {
4✔
113
        case feeRate := <-m.relayFee:
4✔
114
                return feeRate
4✔
115
        case <-m.quit:
×
116
                return 0
×
117
        }
118
}
119

120
func (m *mockFeeEstimator) Start() error {
×
121
        return nil
×
122
}
×
123
func (m *mockFeeEstimator) Stop() error {
2✔
124
        close(m.quit)
2✔
125
        return nil
2✔
126
}
2✔
127

128
var _ chainfee.Estimator = (*mockFeeEstimator)(nil)
129

130
type mockForwardingLog struct {
131
        sync.Mutex
132

133
        events map[time.Time]channeldb.ForwardingEvent
134
}
135

136
func (m *mockForwardingLog) AddForwardingEvents(events []channeldb.ForwardingEvent) error {
17✔
137
        m.Lock()
17✔
138
        defer m.Unlock()
17✔
139

17✔
140
        for _, event := range events {
46✔
141
                m.events[event.Timestamp] = event
29✔
142
        }
29✔
143

144
        return nil
17✔
145
}
146

147
type mockServer struct {
148
        started  int32 // To be used atomically.
149
        shutdown int32 // To be used atomically.
150
        wg       sync.WaitGroup
151
        quit     chan struct{}
152

153
        t testing.TB
154

155
        name             string
156
        messages         chan lnwire.Message
157
        protocolTraceMtx sync.Mutex
158
        protocolTrace    []lnwire.Message
159

160
        id         [33]byte
161
        htlcSwitch *Switch
162

163
        registry         *mockInvoiceRegistry
164
        pCache           *mockPreimageCache
165
        interceptorFuncs []messageInterceptor
166
}
167

168
var _ lnpeer.Peer = (*mockServer)(nil)
169

170
func initSwitchWithDB(startingHeight uint32, db *channeldb.DB) (*Switch, error) {
341✔
171
        signAliasUpdate := func(u *lnwire.ChannelUpdate1) (*ecdsa.Signature,
341✔
172
                error) {
354✔
173

13✔
174
                return testSig, nil
13✔
175
        }
13✔
176

177
        cfg := Config{
341✔
178
                DB:                   db,
341✔
179
                FetchAllOpenChannels: db.ChannelStateDB().FetchAllOpenChannels,
341✔
180
                FetchAllChannels:     db.ChannelStateDB().FetchAllChannels,
341✔
181
                FetchClosedChannels:  db.ChannelStateDB().FetchClosedChannels,
341✔
182
                SwitchPackager:       channeldb.NewSwitchPackager(),
341✔
183
                FwdingLog: &mockForwardingLog{
341✔
184
                        events: make(map[time.Time]channeldb.ForwardingEvent),
341✔
185
                },
341✔
186
                FetchLastChannelUpdate: func(scid lnwire.ShortChannelID) (
341✔
187
                        *lnwire.ChannelUpdate1, error) {
363✔
188

22✔
189
                        return &lnwire.ChannelUpdate1{
22✔
190
                                ShortChannelID: scid,
22✔
191
                        }, nil
22✔
192
                },
22✔
193
                Notifier: &mock.ChainNotifier{
194
                        SpendChan: make(chan *chainntnfs.SpendDetail),
195
                        EpochChan: make(chan *chainntnfs.BlockEpoch),
196
                        ConfChan:  make(chan *chainntnfs.TxConfirmation),
197
                },
198
                FwdEventTicker: ticker.NewForce(
199
                        DefaultFwdEventInterval,
200
                ),
201
                LogEventTicker:         ticker.NewForce(DefaultLogInterval),
202
                AckEventTicker:         ticker.NewForce(DefaultAckInterval),
203
                HtlcNotifier:           &mockHTLCNotifier{},
204
                Clock:                  clock.NewDefaultClock(),
205
                MailboxDeliveryTimeout: time.Hour,
206
                MaxFeeExposure:         DefaultMaxFeeExposure,
207
                SignAliasUpdate:        signAliasUpdate,
208
                IsAlias:                isAlias,
209
        }
210

211
        return New(cfg, startingHeight)
341✔
212
}
213

214
func initSwitchWithTempDB(t testing.TB, startingHeight uint32) (*Switch,
215
        error) {
149✔
216

149✔
217
        tempPath := filepath.Join(t.TempDir(), "switchdb")
149✔
218
        db := channeldb.OpenForTesting(t, tempPath)
149✔
219

149✔
220
        s, err := initSwitchWithDB(startingHeight, db)
149✔
221
        if err != nil {
149✔
222
                return nil, err
×
223
        }
×
224

225
        return s, nil
149✔
226
}
227

228
func newMockServer(t testing.TB, name string, startingHeight uint32,
229
        db *channeldb.DB, defaultDelta uint32) (*mockServer, error) {
226✔
230

226✔
231
        var id [33]byte
226✔
232
        h := sha256.Sum256([]byte(name))
226✔
233
        copy(id[:], h[:])
226✔
234

226✔
235
        pCache := newMockPreimageCache()
226✔
236

226✔
237
        var (
226✔
238
                htlcSwitch *Switch
226✔
239
                err        error
226✔
240
        )
226✔
241
        if db == nil {
323✔
242
                htlcSwitch, err = initSwitchWithTempDB(t, startingHeight)
97✔
243
        } else {
226✔
244
                htlcSwitch, err = initSwitchWithDB(startingHeight, db)
129✔
245
        }
129✔
246
        if err != nil {
226✔
247
                return nil, err
×
248
        }
×
249

250
        t.Cleanup(func() { _ = htlcSwitch.Stop() })
452✔
251

252
        registry := newMockRegistry(t)
226✔
253

226✔
254
        return &mockServer{
226✔
255
                t:                t,
226✔
256
                id:               id,
226✔
257
                name:             name,
226✔
258
                messages:         make(chan lnwire.Message, 3000),
226✔
259
                quit:             make(chan struct{}),
226✔
260
                registry:         registry,
226✔
261
                htlcSwitch:       htlcSwitch,
226✔
262
                pCache:           pCache,
226✔
263
                interceptorFuncs: make([]messageInterceptor, 0),
226✔
264
        }, nil
226✔
265
}
266

267
func (s *mockServer) Start() error {
129✔
268
        if !atomic.CompareAndSwapInt32(&s.started, 0, 1) {
129✔
269
                return errors.New("mock server already started")
×
270
        }
×
271

272
        if err := s.htlcSwitch.Start(); err != nil {
129✔
273
                return err
×
274
        }
×
275

276
        s.wg.Add(1)
129✔
277
        go func() {
258✔
278
                defer s.wg.Done()
129✔
279

129✔
280
                defer func() {
252✔
281
                        s.htlcSwitch.Stop()
123✔
282
                }()
123✔
283

284
                for {
3,582✔
285
                        select {
3,453✔
286
                        case msg := <-s.messages:
3,324✔
287
                                s.protocolTraceMtx.Lock()
3,324✔
288
                                s.protocolTrace = append(s.protocolTrace, msg)
3,324✔
289
                                s.protocolTraceMtx.Unlock()
3,324✔
290

3,324✔
291
                                var shouldSkip bool
3,324✔
292

3,324✔
293
                                for _, interceptor := range s.interceptorFuncs {
3,424✔
294
                                        skip, err := interceptor(msg)
100✔
295
                                        if err != nil {
100✔
296
                                                s.t.Fatalf("%v: error in the "+
×
297
                                                        "interceptor: %v", s.name, err)
×
298
                                                return
×
299
                                        }
×
300
                                        shouldSkip = shouldSkip || skip
100✔
301
                                }
302

303
                                if shouldSkip {
3,329✔
304
                                        continue
5✔
305
                                }
306

307
                                if err := s.readHandler(msg); err != nil {
3,319✔
308
                                        s.t.Fatal(err)
×
309
                                        return
×
310
                                }
×
311
                        case <-s.quit:
123✔
312
                                return
123✔
313
                        }
314
                }
315
        }()
316

317
        return nil
129✔
318
}
319

320
func (s *mockServer) QuitSignal() <-chan struct{} {
×
321
        return s.quit
×
322
}
×
323

324
// mockHopIterator represents the test version of hop iterator which instead
325
// of encrypting the path in onion blob just stores the path as a list of hops.
326
type mockHopIterator struct {
327
        hops []*hop.Payload
328
}
329

330
func newMockHopIterator(hops ...*hop.Payload) hop.Iterator {
792✔
331
        return &mockHopIterator{hops: hops}
792✔
332
}
792✔
333

334
func (r *mockHopIterator) HopPayload() (*hop.Payload, hop.RouteRole, error) {
431✔
335
        h := r.hops[0]
431✔
336
        r.hops = r.hops[1:]
431✔
337
        return h, hop.RouteRoleCleartext, nil
431✔
338
}
431✔
339

340
func (r *mockHopIterator) ExtraOnionBlob() []byte {
×
341
        return nil
×
342
}
×
343

344
func (r *mockHopIterator) ExtractEncrypterParams(
345
        extracter hop.SharedSecretGenerator) (*btcec.PublicKey, sphinx.Hash256,
346
        lnwire.BlindingPointRecord, lnwire.FailCode) {
431✔
347

431✔
348
        sharedSecret, failCode := extracter(nil)
431✔
349
        if failCode != lnwire.CodeNone {
432✔
350
                return nil, sphinx.Hash256{}, lnwire.BlindingPointRecord{},
1✔
351
                        failCode
1✔
352
        }
1✔
353

354
        return &btcec.PublicKey{}, sharedSecret, lnwire.BlindingPointRecord{},
430✔
355
                lnwire.CodeNone
430✔
356
}
357

358
func (r *mockHopIterator) EncodeNextHop(w io.Writer) error {
394✔
359
        var hopLength [4]byte
394✔
360
        binary.BigEndian.PutUint32(hopLength[:], uint32(len(r.hops)))
394✔
361

394✔
362
        if _, err := w.Write(hopLength[:]); err != nil {
394✔
363
                return err
×
364
        }
×
365

366
        for _, hop := range r.hops {
826✔
367
                fwdInfo := hop.ForwardingInfo()
432✔
368
                if err := encodeFwdInfo(w, &fwdInfo); err != nil {
432✔
369
                        return err
×
370
                }
×
371
        }
372

373
        return nil
394✔
374
}
375

376
func encodeFwdInfo(w io.Writer, f *hop.ForwardingInfo) error {
432✔
377
        if err := binary.Write(w, binary.BigEndian, f.NextHop); err != nil {
432✔
378
                return err
×
379
        }
×
380

381
        if err := binary.Write(w, binary.BigEndian, f.AmountToForward); err != nil {
432✔
382
                return err
×
383
        }
×
384

385
        if err := binary.Write(w, binary.BigEndian, f.OutgoingCTLV); err != nil {
432✔
386
                return err
×
387
        }
×
388

389
        return nil
432✔
390
}
391

392
var _ hop.Iterator = (*mockHopIterator)(nil)
393

394
// mockObfuscator mock implementation of the failure obfuscator which only
395
// encodes the failure and do not makes any onion obfuscation.
396
type mockObfuscator struct {
397
        ogPacket *sphinx.OnionPacket
398
        failure  lnwire.FailureMessage
399
}
400

401
// NewMockObfuscator initializes a dummy mockObfuscator used for testing.
402
func NewMockObfuscator() hop.ErrorEncrypter {
1,679✔
403
        return &mockObfuscator{}
1,679✔
404
}
1,679✔
405

406
func (o *mockObfuscator) OnionPacket() *sphinx.OnionPacket {
×
407
        return o.ogPacket
×
408
}
×
409

410
func (o *mockObfuscator) Type() hop.EncrypterType {
232✔
411
        return hop.EncrypterTypeMock
232✔
412
}
232✔
413

414
func (o *mockObfuscator) Encode(w io.Writer) error {
112✔
415
        return nil
112✔
416
}
112✔
417

418
func (o *mockObfuscator) Decode(r io.Reader) error {
66✔
419
        return nil
66✔
420
}
66✔
421

422
func (o *mockObfuscator) Reextract(extracter hop.SharedSecretGenerator) error {
66✔
423
        return nil
66✔
424
}
66✔
425

426
var fakeHmac = []byte("hmachmachmachmachmachmachmachmac")
427

428
func (o *mockObfuscator) EncryptFirstHop(failure lnwire.FailureMessage) (
429
        lnwire.OpaqueReason, []byte, error) {
145✔
430

145✔
431
        o.failure = failure
145✔
432

145✔
433
        var b bytes.Buffer
145✔
434
        b.Write(fakeHmac)
145✔
435

145✔
436
        if err := lnwire.EncodeFailure(&b, failure, 0); err != nil {
145✔
NEW
437
                return nil, nil, err
×
438
        }
×
439

440
        return b.Bytes(), nil, nil
145✔
441
}
442

443
func (o *mockObfuscator) IntermediateEncrypt(reason lnwire.OpaqueReason,
444
        attrData []byte) (lnwire.OpaqueReason, []byte, error) {
6✔
445

6✔
446
        return reason, nil, nil
6✔
447
}
6✔
448

449
func (o *mockObfuscator) EncryptMalformedError(
450
        reason lnwire.OpaqueReason) (lnwire.OpaqueReason, []byte, error) {
2✔
451

2✔
452
        var b bytes.Buffer
2✔
453
        b.Write(fakeHmac)
2✔
454

2✔
455
        b.Write(reason)
2✔
456

2✔
457
        return b.Bytes(), nil, nil
2✔
458
}
2✔
459

460
// mockDeobfuscator mock implementation of the failure deobfuscator which
461
// only decodes the failure do not makes any onion obfuscation.
462
type mockDeobfuscator struct{}
463

464
func newMockDeobfuscator() ErrorDecrypter {
304✔
465
        return &mockDeobfuscator{}
304✔
466
}
304✔
467

468
func (o *mockDeobfuscator) DecryptError(reason lnwire.OpaqueReason,
469
        attrData []byte) (*ForwardingError, error) {
119✔
470

119✔
471
        if !bytes.Equal(reason[:32], fakeHmac) {
119✔
472
                return nil, errors.New("fake decryption error")
×
473
        }
×
474
        reason = reason[32:]
119✔
475

119✔
476
        r := bytes.NewReader(reason)
119✔
477
        failure, err := lnwire.DecodeFailure(r, 0)
119✔
478
        if err != nil {
119✔
479
                return nil, err
×
480
        }
×
481

482
        return NewForwardingError(failure, 1, nil), nil
119✔
483
}
484

485
var _ ErrorDecrypter = (*mockDeobfuscator)(nil)
486

487
// mockIteratorDecoder test version of hop iterator decoder which decodes the
488
// encoded array of hops.
489
type mockIteratorDecoder struct {
490
        mu sync.RWMutex
491

492
        responses map[[32]byte][]hop.DecodeHopIteratorResponse
493

494
        decodeFail bool
495
}
496

497
func newMockIteratorDecoder() *mockIteratorDecoder {
176✔
498
        return &mockIteratorDecoder{
176✔
499
                responses: make(map[[32]byte][]hop.DecodeHopIteratorResponse),
176✔
500
        }
176✔
501
}
176✔
502

503
func (p *mockIteratorDecoder) DecodeHopIterator(r io.Reader, rHash []byte,
504
        cltv uint32) (hop.Iterator, lnwire.FailCode) {
433✔
505

433✔
506
        var b [4]byte
433✔
507
        _, err := r.Read(b[:])
433✔
508
        if err != nil {
433✔
509
                return nil, lnwire.CodeTemporaryChannelFailure
×
510
        }
×
511
        hopLength := binary.BigEndian.Uint32(b[:])
433✔
512

433✔
513
        hops := make([]*hop.Payload, hopLength)
433✔
514
        for i := uint32(0); i < hopLength; i++ {
902✔
515
                var f hop.ForwardingInfo
469✔
516
                if err := decodeFwdInfo(r, &f); err != nil {
469✔
517
                        return nil, lnwire.CodeTemporaryChannelFailure
×
518
                }
×
519

520
                var nextHopBytes [8]byte
469✔
521
                binary.BigEndian.PutUint64(nextHopBytes[:], f.NextHop.ToUint64())
469✔
522

469✔
523
                hops[i] = hop.NewLegacyPayload(&sphinx.HopData{
469✔
524
                        Realm:         [1]byte{}, // hop.BitcoinNetwork
469✔
525
                        NextAddress:   nextHopBytes,
469✔
526
                        ForwardAmount: uint64(f.AmountToForward),
469✔
527
                        OutgoingCltv:  f.OutgoingCTLV,
469✔
528
                })
469✔
529
        }
530

531
        return newMockHopIterator(hops...), lnwire.CodeNone
433✔
532
}
533

534
func (p *mockIteratorDecoder) DecodeHopIterators(id []byte,
535
        reqs []hop.DecodeHopIteratorRequest, _ bool) (
536
        []hop.DecodeHopIteratorResponse, error) {
1,156✔
537

1,156✔
538
        idHash := sha256.Sum256(id)
1,156✔
539

1,156✔
540
        p.mu.RLock()
1,156✔
541
        if resps, ok := p.responses[idHash]; ok {
1,156✔
542
                p.mu.RUnlock()
×
543
                return resps, nil
×
544
        }
×
545
        p.mu.RUnlock()
1,156✔
546

1,156✔
547
        batchSize := len(reqs)
1,156✔
548

1,156✔
549
        resps := make([]hop.DecodeHopIteratorResponse, 0, batchSize)
1,156✔
550
        for _, req := range reqs {
1,589✔
551
                iterator, failcode := p.DecodeHopIterator(
433✔
552
                        req.OnionReader, req.RHash, req.IncomingCltv,
433✔
553
                )
433✔
554

433✔
555
                if p.decodeFail {
435✔
556
                        failcode = lnwire.CodeTemporaryChannelFailure
2✔
557
                }
2✔
558

559
                resp := hop.DecodeHopIteratorResponse{
433✔
560
                        HopIterator: iterator,
433✔
561
                        FailCode:    failcode,
433✔
562
                }
433✔
563
                resps = append(resps, resp)
433✔
564
        }
565

566
        p.mu.Lock()
1,156✔
567
        p.responses[idHash] = resps
1,156✔
568
        p.mu.Unlock()
1,156✔
569

1,156✔
570
        return resps, nil
1,156✔
571
}
572

573
func decodeFwdInfo(r io.Reader, f *hop.ForwardingInfo) error {
469✔
574
        if err := binary.Read(r, binary.BigEndian, &f.NextHop); err != nil {
469✔
575
                return err
×
576
        }
×
577

578
        if err := binary.Read(r, binary.BigEndian, &f.AmountToForward); err != nil {
469✔
579
                return err
×
580
        }
×
581

582
        if err := binary.Read(r, binary.BigEndian, &f.OutgoingCTLV); err != nil {
469✔
583
                return err
×
584
        }
×
585

586
        return nil
469✔
587
}
588

589
// messageInterceptor is function that handles the incoming peer messages and
590
// may decide should the peer skip the message or not.
591
type messageInterceptor func(m lnwire.Message) (bool, error)
592

593
// Record is used to set the function which will be triggered when new
594
// lnwire message was received.
595
func (s *mockServer) intersect(f messageInterceptor) {
16✔
596
        s.interceptorFuncs = append(s.interceptorFuncs, f)
16✔
597
}
16✔
598

599
func (s *mockServer) SendMessage(sync bool, msgs ...lnwire.Message) error {
3,324✔
600

3,324✔
601
        for _, msg := range msgs {
6,648✔
602
                select {
3,324✔
603
                case s.messages <- msg:
3,324✔
604
                case <-s.quit:
×
605
                        return errors.New("server is stopped")
×
606
                }
607
        }
608

609
        return nil
3,324✔
610
}
611

612
func (s *mockServer) SendMessageLazy(sync bool, msgs ...lnwire.Message) error {
×
613
        panic("not implemented")
×
614
}
615

616
func (s *mockServer) readHandler(message lnwire.Message) error {
3,319✔
617
        var targetChan lnwire.ChannelID
3,319✔
618

3,319✔
619
        switch msg := message.(type) {
3,319✔
620
        case *lnwire.UpdateAddHTLC:
423✔
621
                targetChan = msg.ChanID
423✔
622
        case *lnwire.UpdateFulfillHTLC:
202✔
623
                targetChan = msg.ChanID
202✔
624
        case *lnwire.UpdateFailHTLC:
117✔
625
                targetChan = msg.ChanID
117✔
626
        case *lnwire.UpdateFailMalformedHTLC:
3✔
627
                targetChan = msg.ChanID
3✔
628
        case *lnwire.RevokeAndAck:
1,118✔
629
                targetChan = msg.ChanID
1,118✔
630
        case *lnwire.CommitSig:
1,119✔
631
                targetChan = msg.ChanID
1,119✔
632
        case *lnwire.ChannelReady:
164✔
633
                // Ignore
164✔
634
                return nil
164✔
635
        case *lnwire.ChannelReestablish:
168✔
636
                targetChan = msg.ChanID
168✔
637
        case *lnwire.UpdateFee:
3✔
638
                targetChan = msg.ChanID
3✔
639
        case *lnwire.Stfu:
2✔
640
                targetChan = msg.ChanID
2✔
641
        default:
×
642
                return fmt.Errorf("unknown message type: %T", msg)
×
643
        }
644

645
        // Dispatch the commitment update message to the proper channel link
646
        // dedicated to this channel. If the link is not found, we will discard
647
        // the message.
648
        link, err := s.htlcSwitch.GetLink(targetChan)
3,155✔
649
        if err != nil {
3,156✔
650
                return nil
1✔
651
        }
1✔
652

653
        // Create goroutine for this, in order to be able to properly stop
654
        // the server when handler stacked (server unavailable)
655
        link.HandleChannelUpdate(message)
3,154✔
656

3,154✔
657
        return nil
3,154✔
658
}
659

660
func (s *mockServer) PubKey() [33]byte {
816✔
661
        return s.id
816✔
662
}
816✔
663

664
func (s *mockServer) IdentityKey() *btcec.PublicKey {
×
665
        pubkey, _ := btcec.ParsePubKey(s.id[:])
×
666
        return pubkey
×
667
}
×
668

669
func (s *mockServer) Address() net.Addr {
×
670
        return nil
×
671
}
×
672

673
func (s *mockServer) AddNewChannel(channel *lnpeer.NewChannel,
674
        cancel <-chan struct{}) error {
×
675

×
676
        return nil
×
677
}
×
678

679
func (s *mockServer) AddPendingChannel(_ lnwire.ChannelID,
680
        cancel <-chan struct{}) error {
×
681

×
682
        return nil
×
683
}
×
684

685
func (s *mockServer) RemovePendingChannel(_ lnwire.ChannelID) error {
×
686
        return nil
×
687
}
×
688

689
func (s *mockServer) WipeChannel(*wire.OutPoint) {}
×
690

691
func (s *mockServer) LocalFeatures() *lnwire.FeatureVector {
×
692
        return nil
×
693
}
×
694

695
func (s *mockServer) RemoteFeatures() *lnwire.FeatureVector {
×
696
        return nil
×
697
}
×
698

699
func (s *mockServer) Disconnect(err error) {}
2✔
700

701
func (s *mockServer) Stop() error {
132✔
702
        if !atomic.CompareAndSwapInt32(&s.shutdown, 0, 1) {
141✔
703
                return nil
9✔
704
        }
9✔
705

706
        close(s.quit)
123✔
707
        s.wg.Wait()
123✔
708

123✔
709
        return nil
123✔
710
}
711

712
func (s *mockServer) String() string {
×
713
        return s.name
×
714
}
×
715

716
type mockChannelLink struct {
717
        htlcSwitch *Switch
718

719
        shortChanID lnwire.ShortChannelID
720

721
        // Only used for zero-conf channels.
722
        realScid lnwire.ShortChannelID
723

724
        aliases []lnwire.ShortChannelID
725

726
        chanID lnwire.ChannelID
727

728
        peer lnpeer.Peer
729

730
        mailBox MailBox
731

732
        packets chan *htlcPacket
733

734
        eligible bool
735

736
        unadvertised bool
737

738
        zeroConf bool
739

740
        optionFeature bool
741

742
        htlcID uint64
743

744
        checkHtlcTransitResult *LinkError
745

746
        checkHtlcForwardResult *LinkError
747

748
        failAliasUpdate func(sid lnwire.ShortChannelID,
749
                incoming bool) *lnwire.ChannelUpdate1
750

751
        confirmedZC bool
752
}
753

754
// completeCircuit is a helper method for adding the finalized payment circuit
755
// to the switch's circuit map. In testing, this should be executed after
756
// receiving an htlc from the downstream packets channel.
757
func (f *mockChannelLink) completeCircuit(pkt *htlcPacket) error {
31✔
758
        switch htlc := pkt.htlc.(type) {
31✔
759
        case *lnwire.UpdateAddHTLC:
14✔
760
                pkt.outgoingChanID = f.shortChanID
14✔
761
                pkt.outgoingHTLCID = f.htlcID
14✔
762
                htlc.ID = f.htlcID
14✔
763

14✔
764
                keystone := Keystone{pkt.inKey(), pkt.outKey()}
14✔
765
                err := f.htlcSwitch.circuits.OpenCircuits(keystone)
14✔
766
                if err != nil {
14✔
767
                        return err
×
768
                }
×
769

770
                f.htlcID++
14✔
771

772
        case *lnwire.UpdateFulfillHTLC, *lnwire.UpdateFailHTLC:
17✔
773
                if pkt.circuit != nil {
27✔
774
                        err := f.htlcSwitch.teardownCircuit(pkt)
10✔
775
                        if err != nil {
10✔
776
                                return err
×
777
                        }
×
778
                }
779
        }
780

781
        f.mailBox.AckPacket(pkt.inKey())
31✔
782

31✔
783
        return nil
31✔
784
}
785

786
func (f *mockChannelLink) deleteCircuit(pkt *htlcPacket) error {
1✔
787
        return f.htlcSwitch.circuits.DeleteCircuits(pkt.inKey())
1✔
788
}
1✔
789

790
func newMockChannelLink(htlcSwitch *Switch, chanID lnwire.ChannelID,
791
        shortChanID, realScid lnwire.ShortChannelID, peer lnpeer.Peer,
792
        eligible, unadvertised, zeroConf, optionFeature bool,
793
) *mockChannelLink {
118✔
794

118✔
795
        aliases := make([]lnwire.ShortChannelID, 0)
118✔
796
        var realConfirmed bool
118✔
797

118✔
798
        if zeroConf {
134✔
799
                aliases = append(aliases, shortChanID)
16✔
800
        }
16✔
801

802
        if realScid != hop.Source {
130✔
803
                realConfirmed = true
12✔
804
        }
12✔
805

806
        return &mockChannelLink{
118✔
807
                htlcSwitch:    htlcSwitch,
118✔
808
                chanID:        chanID,
118✔
809
                shortChanID:   shortChanID,
118✔
810
                realScid:      realScid,
118✔
811
                peer:          peer,
118✔
812
                eligible:      eligible,
118✔
813
                unadvertised:  unadvertised,
118✔
814
                zeroConf:      zeroConf,
118✔
815
                optionFeature: optionFeature,
118✔
816
                aliases:       aliases,
118✔
817
                confirmedZC:   realConfirmed,
118✔
818
        }
118✔
819
}
820

821
// addAlias is not part of any interface method.
822
func (f *mockChannelLink) addAlias(alias lnwire.ShortChannelID) {
22✔
823
        f.aliases = append(f.aliases, alias)
22✔
824
}
22✔
825

826
func (f *mockChannelLink) handleSwitchPacket(pkt *htlcPacket) error {
40✔
827
        f.mailBox.AddPacket(pkt)
40✔
828
        return nil
40✔
829
}
40✔
830

831
func (f *mockChannelLink) getDustSum(whoseCommit lntypes.ChannelParty,
832
        dryRunFee fn.Option[chainfee.SatPerKWeight]) lnwire.MilliSatoshi {
150✔
833

150✔
834
        return 0
150✔
835
}
150✔
836

837
func (f *mockChannelLink) getFeeRate() chainfee.SatPerKWeight {
76✔
838
        return 0
76✔
839
}
76✔
840

841
func (f *mockChannelLink) getDustClosure() dustClosure {
198✔
842
        dustLimit := btcutil.Amount(400)
198✔
843
        return dustHelper(
198✔
844
                channeldb.SingleFunderTweaklessBit, dustLimit, dustLimit,
198✔
845
        )
198✔
846
}
198✔
847

848
func (f *mockChannelLink) getCommitFee(remote bool) btcutil.Amount {
×
849
        return 0
×
850
}
×
851

852
func (f *mockChannelLink) HandleChannelUpdate(lnwire.Message) {
×
853
}
×
854

855
func (f *mockChannelLink) UpdateForwardingPolicy(_ models.ForwardingPolicy) {
×
856
}
×
857
func (f *mockChannelLink) CheckHtlcForward([32]byte, lnwire.MilliSatoshi,
858
        lnwire.MilliSatoshi, uint32, uint32, models.InboundFee, uint32,
859
        lnwire.ShortChannelID, lnwire.CustomRecords) *LinkError {
37✔
860

37✔
861
        return f.checkHtlcForwardResult
37✔
862
}
37✔
863

864
func (f *mockChannelLink) CheckHtlcTransit(payHash [32]byte,
865
        amt lnwire.MilliSatoshi, timeout uint32,
866
        heightNow uint32, _ lnwire.CustomRecords) *LinkError {
9✔
867

9✔
868
        return f.checkHtlcTransitResult
9✔
869
}
9✔
870

871
func (f *mockChannelLink) Stats() (
872
        uint64, lnwire.MilliSatoshi, lnwire.MilliSatoshi) {
2✔
873

2✔
874
        return 0, 0, 0
2✔
875
}
2✔
876

877
func (f *mockChannelLink) AttachMailBox(mailBox MailBox) {
122✔
878
        f.mailBox = mailBox
122✔
879
        f.packets = mailBox.PacketOutBox()
122✔
880
        mailBox.SetDustClosure(f.getDustClosure())
122✔
881
}
122✔
882

883
func (f *mockChannelLink) attachFailAliasUpdate(closure func(
884
        sid lnwire.ShortChannelID, incoming bool) *lnwire.ChannelUpdate1) {
122✔
885

122✔
886
        f.failAliasUpdate = closure
122✔
887
}
122✔
888

889
func (f *mockChannelLink) getAliases() []lnwire.ShortChannelID {
130✔
890
        return f.aliases
130✔
891
}
130✔
892

893
func (f *mockChannelLink) isZeroConf() bool {
121✔
894
        return f.zeroConf
121✔
895
}
121✔
896

897
func (f *mockChannelLink) negotiatedAliasFeature() bool {
105✔
898
        return f.optionFeature
105✔
899
}
105✔
900

901
func (f *mockChannelLink) confirmedScid() lnwire.ShortChannelID {
13✔
902
        return f.realScid
13✔
903
}
13✔
904

905
func (f *mockChannelLink) zeroConfConfirmed() bool {
16✔
906
        return f.confirmedZC
16✔
907
}
16✔
908

909
func (f *mockChannelLink) Start() error {
122✔
910
        f.mailBox.ResetMessages()
122✔
911
        f.mailBox.ResetPackets()
122✔
912
        return nil
122✔
913
}
122✔
914

915
func (f *mockChannelLink) ChanID() lnwire.ChannelID {
926✔
916
        return f.chanID
926✔
917
}
926✔
918

919
func (f *mockChannelLink) ShortChanID() lnwire.ShortChannelID {
693✔
920
        return f.shortChanID
693✔
921
}
693✔
922

923
func (f *mockChannelLink) Bandwidth() lnwire.MilliSatoshi {
×
924
        return 99999999
×
925
}
×
926

927
func (f *mockChannelLink) PeerPubKey() [33]byte {
280✔
928
        return f.peer.PubKey()
280✔
929
}
280✔
930

931
func (f *mockChannelLink) ChannelPoint() wire.OutPoint {
×
932
        return wire.OutPoint{}
×
933
}
×
934

935
func (f *mockChannelLink) Stop()                                        {}
122✔
936
func (f *mockChannelLink) EligibleToForward() bool                      { return f.eligible }
53✔
937
func (f *mockChannelLink) MayAddOutgoingHtlc(lnwire.MilliSatoshi) error { return nil }
×
938
func (f *mockChannelLink) setLiveShortChanID(sid lnwire.ShortChannelID) { f.shortChanID = sid }
×
939
func (f *mockChannelLink) IsUnadvertised() bool                         { return f.unadvertised }
5✔
940
func (f *mockChannelLink) UpdateShortChanID() (lnwire.ShortChannelID, error) {
1✔
941
        f.eligible = true
1✔
942
        return f.shortChanID, nil
1✔
943
}
1✔
944

945
func (f *mockChannelLink) EnableAdds(linkDirection LinkDirection) bool {
×
946
        // TODO(proofofkeags): Implement
×
947
        return true
×
948
}
×
949

950
func (f *mockChannelLink) DisableAdds(linkDirection LinkDirection) bool {
×
951
        // TODO(proofofkeags): Implement
×
952
        return true
×
953
}
×
954
func (f *mockChannelLink) IsFlushing(linkDirection LinkDirection) bool {
×
955
        // TODO(proofofkeags): Implement
×
956
        return false
×
957
}
×
958
func (f *mockChannelLink) OnFlushedOnce(func()) {
×
959
        // TODO(proofofkeags): Implement
×
960
}
×
961
func (f *mockChannelLink) OnCommitOnce(LinkDirection, func()) {
×
962
        // TODO(proofofkeags): Implement
×
963
}
×
964
func (f *mockChannelLink) InitStfu() <-chan fn.Result[lntypes.ChannelParty] {
×
965
        // TODO(proofofkeags): Implement
×
966
        c := make(chan fn.Result[lntypes.ChannelParty], 1)
×
967

×
968
        c <- fn.Errf[lntypes.ChannelParty]("InitStfu not implemented")
×
969

×
970
        return c
×
971
}
×
972

973
func (f *mockChannelLink) FundingCustomBlob() fn.Option[tlv.Blob] {
×
974
        return fn.None[tlv.Blob]()
×
975
}
×
976

977
func (f *mockChannelLink) CommitmentCustomBlob() fn.Option[tlv.Blob] {
×
978
        return fn.None[tlv.Blob]()
×
979
}
×
980

981
// AuxBandwidth returns the bandwidth that can be used for a channel,
982
// expressed in milli-satoshi. This might be different from the regular
983
// BTC bandwidth for custom channels. This will always return fn.None()
984
// for a regular (non-custom) channel.
985
func (f *mockChannelLink) AuxBandwidth(lnwire.MilliSatoshi,
986
        lnwire.ShortChannelID,
987
        fn.Option[tlv.Blob], AuxTrafficShaper) fn.Result[OptionalBandwidth] {
×
988

×
989
        return fn.Ok(OptionalBandwidth{})
×
990
}
×
991

992
var _ ChannelLink = (*mockChannelLink)(nil)
993

994
const testInvoiceCltvExpiry = 6
995

996
type mockInvoiceRegistry struct {
997
        settleChan chan lntypes.Hash
998

999
        registry *invoices.InvoiceRegistry
1000
}
1001

1002
type mockChainNotifier struct {
1003
        chainntnfs.ChainNotifier
1004
}
1005

1006
// RegisterBlockEpochNtfn mocks a successful call to register block
1007
// notifications.
1008
func (m *mockChainNotifier) RegisterBlockEpochNtfn(*chainntnfs.BlockEpoch) (
1009
        *chainntnfs.BlockEpochEvent, error) {
258✔
1010

258✔
1011
        return &chainntnfs.BlockEpochEvent{
258✔
1012
                Cancel: func() {},
258✔
1013
        }, nil
1014
}
1015

1016
func newMockRegistry(t testing.TB) *mockInvoiceRegistry {
258✔
1017
        cdb := channeldb.OpenForTesting(t, t.TempDir())
258✔
1018

258✔
1019
        modifierMock := &invoices.MockHtlcModifier{}
258✔
1020
        registry := invoices.NewRegistry(
258✔
1021
                cdb,
258✔
1022
                invoices.NewInvoiceExpiryWatcher(
258✔
1023
                        clock.NewDefaultClock(), 0, 0, nil,
258✔
1024
                        &mockChainNotifier{},
258✔
1025
                ),
258✔
1026
                &invoices.RegistryConfig{
258✔
1027
                        FinalCltvRejectDelta: 5,
258✔
1028
                        HtlcInterceptor:      modifierMock,
258✔
1029
                },
258✔
1030
        )
258✔
1031
        registry.Start()
258✔
1032

258✔
1033
        return &mockInvoiceRegistry{
258✔
1034
                registry: registry,
258✔
1035
        }
258✔
1036
}
258✔
1037

1038
func (i *mockInvoiceRegistry) LookupInvoice(ctx context.Context,
1039
        rHash lntypes.Hash) (invoices.Invoice, error) {
14✔
1040

14✔
1041
        return i.registry.LookupInvoice(ctx, rHash)
14✔
1042
}
14✔
1043

1044
func (i *mockInvoiceRegistry) SettleHodlInvoice(
1045
        ctx context.Context, preimage lntypes.Preimage) error {
54✔
1046

54✔
1047
        return i.registry.SettleHodlInvoice(ctx, preimage)
54✔
1048
}
54✔
1049

1050
func (i *mockInvoiceRegistry) NotifyExitHopHtlc(rhash lntypes.Hash,
1051
        amt lnwire.MilliSatoshi, expiry uint32, currentHeight int32,
1052
        circuitKey models.CircuitKey, hodlChan chan<- interface{},
1053
        wireCustomRecords lnwire.CustomRecords,
1054
        payload invoices.Payload) (invoices.HtlcResolution, error) {
202✔
1055

202✔
1056
        event, err := i.registry.NotifyExitHopHtlc(
202✔
1057
                rhash, amt, expiry, currentHeight, circuitKey,
202✔
1058
                hodlChan, wireCustomRecords, payload,
202✔
1059
        )
202✔
1060
        if err != nil {
202✔
1061
                return nil, err
×
1062
        }
×
1063
        if i.settleChan != nil {
209✔
1064
                i.settleChan <- rhash
7✔
1065
        }
7✔
1066

1067
        return event, nil
202✔
1068
}
1069

1070
func (i *mockInvoiceRegistry) CancelInvoice(ctx context.Context,
1071
        payHash lntypes.Hash) error {
2✔
1072

2✔
1073
        return i.registry.CancelInvoice(ctx, payHash)
2✔
1074
}
2✔
1075

1076
func (i *mockInvoiceRegistry) AddInvoice(ctx context.Context,
1077
        invoice invoices.Invoice, paymentHash lntypes.Hash) error {
327✔
1078

327✔
1079
        _, err := i.registry.AddInvoice(ctx, &invoice, paymentHash)
327✔
1080
        return err
327✔
1081
}
327✔
1082

1083
func (i *mockInvoiceRegistry) HodlUnsubscribeAll(
1084
        subscriber chan<- interface{}) {
202✔
1085

202✔
1086
        i.registry.HodlUnsubscribeAll(subscriber)
202✔
1087
}
202✔
1088

1089
var _ InvoiceDatabase = (*mockInvoiceRegistry)(nil)
1090

1091
type mockCircuitMap struct {
1092
        lookup chan *PaymentCircuit
1093
}
1094

1095
var _ CircuitMap = (*mockCircuitMap)(nil)
1096

1097
func (m *mockCircuitMap) OpenCircuits(...Keystone) error {
×
1098
        return nil
×
1099
}
×
1100

1101
func (m *mockCircuitMap) TrimOpenCircuits(chanID lnwire.ShortChannelID,
1102
        start uint64) error {
×
1103
        return nil
×
1104
}
×
1105

1106
func (m *mockCircuitMap) DeleteCircuits(inKeys ...CircuitKey) error {
×
1107
        return nil
×
1108
}
×
1109

1110
func (m *mockCircuitMap) CommitCircuits(
1111
        circuit ...*PaymentCircuit) (*CircuitFwdActions, error) {
×
1112

×
1113
        return nil, nil
×
1114
}
×
1115

1116
func (m *mockCircuitMap) CloseCircuit(outKey CircuitKey) (*PaymentCircuit,
1117
        error) {
×
1118
        return nil, nil
×
1119
}
×
1120

1121
func (m *mockCircuitMap) FailCircuit(inKey CircuitKey) (*PaymentCircuit,
1122
        error) {
×
1123
        return nil, nil
×
1124
}
×
1125

1126
func (m *mockCircuitMap) LookupCircuit(inKey CircuitKey) *PaymentCircuit {
3✔
1127
        return <-m.lookup
3✔
1128
}
3✔
1129

1130
func (m *mockCircuitMap) LookupOpenCircuit(outKey CircuitKey) *PaymentCircuit {
×
1131
        return nil
×
1132
}
×
1133

1134
func (m *mockCircuitMap) LookupByPaymentHash(hash [32]byte) []*PaymentCircuit {
×
1135
        return nil
×
1136
}
×
1137

1138
func (m *mockCircuitMap) NumPending() int {
×
1139
        return 0
×
1140
}
×
1141

1142
func (m *mockCircuitMap) NumOpen() int {
×
1143
        return 0
×
1144
}
×
1145

1146
var _ htlcNotifier = (*mockHTLCNotifier)(nil)
1147

1148
type mockHTLCNotifier struct {
1149
        htlcNotifier //nolint:unused
1150
}
1151

1152
func (h *mockHTLCNotifier) NotifyForwardingEvent(key HtlcKey, info HtlcInfo,
1153
        eventType HtlcEventType) {
468✔
1154

468✔
1155
}
468✔
1156

1157
func (h *mockHTLCNotifier) NotifyLinkFailEvent(key HtlcKey, info HtlcInfo,
1158
        eventType HtlcEventType, linkErr *LinkError,
1159
        incoming bool) {
118✔
1160

118✔
1161
}
118✔
1162

1163
func (h *mockHTLCNotifier) NotifyForwardingFailEvent(key HtlcKey,
1164
        eventType HtlcEventType) {
125✔
1165

125✔
1166
}
125✔
1167

1168
func (h *mockHTLCNotifier) NotifySettleEvent(key HtlcKey,
1169
        preimage lntypes.Preimage, eventType HtlcEventType) {
394✔
1170

394✔
1171
}
394✔
1172

1173
func (h *mockHTLCNotifier) NotifyFinalHtlcEvent(key models.CircuitKey,
1174
        info channeldb.FinalHtlcInfo) {
325✔
1175

325✔
1176
}
325✔
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