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

lightningnetwork / lnd / 12026968820

26 Nov 2024 08:48AM UTC coverage: 49.896% (-9.1%) from 58.999%
12026968820

Pull #9303

github

yyforyongyu
lnwallet: add debug logs
Pull Request #9303: htlcswitch+routing: handle nil pointer dereference properly

20 of 23 new or added lines in 4 files covered. (86.96%)

25375 existing lines in 428 files now uncovered.

99993 of 200404 relevant lines covered (49.9%)

2.07 hits per line

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

0.0
/htlcswitch/test_utils.go
1
package htlcswitch
2

3
import (
4
        "bytes"
5
        "context"
6
        crand "crypto/rand"
7
        "crypto/sha256"
8
        "encoding/binary"
9
        "encoding/hex"
10
        "fmt"
11
        "net"
12
        "os"
13
        "runtime"
14
        "runtime/pprof"
15
        "sync/atomic"
16
        "testing"
17
        "time"
18

19
        "github.com/btcsuite/btcd/btcec/v2"
20
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
21
        "github.com/btcsuite/btcd/btcutil"
22
        "github.com/btcsuite/btcd/chaincfg/chainhash"
23
        "github.com/btcsuite/btcd/wire"
24
        "github.com/go-errors/errors"
25
        sphinx "github.com/lightningnetwork/lightning-onion"
26
        "github.com/lightningnetwork/lnd/channeldb"
27
        "github.com/lightningnetwork/lnd/channeldb/models"
28
        "github.com/lightningnetwork/lnd/contractcourt"
29
        "github.com/lightningnetwork/lnd/htlcswitch/hop"
30
        "github.com/lightningnetwork/lnd/input"
31
        "github.com/lightningnetwork/lnd/invoices"
32
        "github.com/lightningnetwork/lnd/keychain"
33
        "github.com/lightningnetwork/lnd/kvdb"
34
        "github.com/lightningnetwork/lnd/lnpeer"
35
        "github.com/lightningnetwork/lnd/lntest/channels"
36
        "github.com/lightningnetwork/lnd/lntest/wait"
37
        "github.com/lightningnetwork/lnd/lntypes"
38
        "github.com/lightningnetwork/lnd/lnwallet"
39
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
40
        "github.com/lightningnetwork/lnd/lnwire"
41
        "github.com/lightningnetwork/lnd/shachain"
42
        "github.com/lightningnetwork/lnd/ticker"
43
        "github.com/stretchr/testify/require"
44
)
45

46
// maxInflightHtlcs specifies the max number of inflight HTLCs. This number is
47
// chosen to be smaller than the default 483 so the test can run faster.
48
const maxInflightHtlcs = 50
49

50
var (
51
        alicePrivKey = []byte("alice priv key")
52
        bobPrivKey   = []byte("bob priv key")
53
        carolPrivKey = []byte("carol priv key")
54

55
        testRBytes, _ = hex.DecodeString("8ce2bc69281ce27da07e6683571319d18e949ddfa2965fb6caa1bf0314f882d7")
56
        testSBytes, _ = hex.DecodeString("299105481d63e0f4bc2a88121167221b6700d72a0ead154c03be696a292d24ae")
57
        testRScalar   = new(btcec.ModNScalar)
58
        testSScalar   = new(btcec.ModNScalar)
59
        _             = testRScalar.SetByteSlice(testRBytes)
60
        _             = testSScalar.SetByteSlice(testSBytes)
61
        testSig       = ecdsa.NewSignature(testRScalar, testSScalar)
62

63
        wireSig, _ = lnwire.NewSigFromSignature(testSig)
64

65
        testBatchTimeout = 50 * time.Millisecond
66
)
67

68
var idSeqNum uint64
69

70
// genID generates a unique tuple to identify a test channel.
UNCOV
71
func genID() (lnwire.ChannelID, lnwire.ShortChannelID) {
×
UNCOV
72
        id := atomic.AddUint64(&idSeqNum, 1)
×
UNCOV
73

×
UNCOV
74
        var scratch [8]byte
×
UNCOV
75

×
UNCOV
76
        binary.BigEndian.PutUint64(scratch[:], id)
×
UNCOV
77
        hash1, _ := chainhash.NewHash(bytes.Repeat(scratch[:], 4))
×
UNCOV
78

×
UNCOV
79
        chanPoint1 := wire.NewOutPoint(hash1, uint32(id))
×
UNCOV
80
        chanID1 := lnwire.NewChanIDFromOutPoint(*chanPoint1)
×
UNCOV
81
        aliceChanID := lnwire.NewShortChanIDFromInt(id)
×
UNCOV
82

×
UNCOV
83
        return chanID1, aliceChanID
×
UNCOV
84
}
×
85

86
// genIDs generates ids for two test channels.
87
func genIDs() (lnwire.ChannelID, lnwire.ChannelID, lnwire.ShortChannelID,
UNCOV
88
        lnwire.ShortChannelID) {
×
UNCOV
89

×
UNCOV
90
        chanID1, aliceChanID := genID()
×
UNCOV
91
        chanID2, bobChanID := genID()
×
UNCOV
92

×
UNCOV
93
        return chanID1, chanID2, aliceChanID, bobChanID
×
UNCOV
94
}
×
95

96
// mockGetChanUpdateMessage helper function which returns topology update of
97
// the channel
98
func mockGetChanUpdateMessage(_ lnwire.ShortChannelID) (*lnwire.ChannelUpdate1,
UNCOV
99
        error) {
×
UNCOV
100

×
UNCOV
101
        return &lnwire.ChannelUpdate1{
×
UNCOV
102
                Signature: wireSig,
×
UNCOV
103
        }, nil
×
UNCOV
104
}
×
105

106
// generateRandomBytes returns securely generated random bytes.
107
// It will return an error if the system's secure random
108
// number generator fails to function correctly, in which
109
// case the caller should not continue.
UNCOV
110
func generateRandomBytes(n int) ([]byte, error) {
×
UNCOV
111
        b := make([]byte, n)
×
UNCOV
112

×
UNCOV
113
        // TODO(roasbeef): should use counter in tests (atomic) rather than
×
UNCOV
114
        // this
×
UNCOV
115

×
UNCOV
116
        _, err := crand.Read(b)
×
UNCOV
117
        // Note that Err == nil only if we read len(b) bytes.
×
UNCOV
118
        if err != nil {
×
119
                return nil, err
×
120
        }
×
121

UNCOV
122
        return b, nil
×
123
}
124

125
type testLightningChannel struct {
126
        channel *lnwallet.LightningChannel
127
        restore func() (*lnwallet.LightningChannel, error)
128
}
129

130
// createTestChannel creates the channel and returns our and remote channels
131
// representations.
132
//
133
// TODO(roasbeef): need to factor out, similar func re-used in many parts of codebase
134
func createTestChannel(t *testing.T, alicePrivKey, bobPrivKey []byte,
135
        aliceAmount, bobAmount, aliceReserve, bobReserve btcutil.Amount,
136
        chanID lnwire.ShortChannelID) (*testLightningChannel,
UNCOV
137
        *testLightningChannel, error) {
×
UNCOV
138

×
UNCOV
139
        aliceKeyPriv, aliceKeyPub := btcec.PrivKeyFromBytes(alicePrivKey)
×
UNCOV
140
        bobKeyPriv, bobKeyPub := btcec.PrivKeyFromBytes(bobPrivKey)
×
UNCOV
141

×
UNCOV
142
        channelCapacity := aliceAmount + bobAmount
×
UNCOV
143
        csvTimeoutAlice := uint32(5)
×
UNCOV
144
        csvTimeoutBob := uint32(4)
×
UNCOV
145
        isAliceInitiator := true
×
UNCOV
146

×
UNCOV
147
        aliceBounds := channeldb.ChannelStateBounds{
×
UNCOV
148
                MaxPendingAmount: lnwire.NewMSatFromSatoshis(
×
UNCOV
149
                        channelCapacity),
×
UNCOV
150
                ChanReserve:      aliceReserve,
×
UNCOV
151
                MinHTLC:          0,
×
UNCOV
152
                MaxAcceptedHtlcs: maxInflightHtlcs,
×
UNCOV
153
        }
×
UNCOV
154
        aliceCommitParams := channeldb.CommitmentParams{
×
UNCOV
155
                DustLimit: btcutil.Amount(200),
×
UNCOV
156
                CsvDelay:  uint16(csvTimeoutAlice),
×
UNCOV
157
        }
×
UNCOV
158

×
UNCOV
159
        bobBounds := channeldb.ChannelStateBounds{
×
UNCOV
160
                MaxPendingAmount: lnwire.NewMSatFromSatoshis(
×
UNCOV
161
                        channelCapacity),
×
UNCOV
162
                ChanReserve:      bobReserve,
×
UNCOV
163
                MinHTLC:          0,
×
UNCOV
164
                MaxAcceptedHtlcs: maxInflightHtlcs,
×
UNCOV
165
        }
×
UNCOV
166
        bobCommitParams := channeldb.CommitmentParams{
×
UNCOV
167
                DustLimit: btcutil.Amount(800),
×
UNCOV
168
                CsvDelay:  uint16(csvTimeoutBob),
×
UNCOV
169
        }
×
UNCOV
170

×
UNCOV
171
        var hash [sha256.Size]byte
×
UNCOV
172
        randomSeed, err := generateRandomBytes(sha256.Size)
×
UNCOV
173
        if err != nil {
×
174
                return nil, nil, err
×
175
        }
×
UNCOV
176
        copy(hash[:], randomSeed)
×
UNCOV
177

×
UNCOV
178
        prevOut := &wire.OutPoint{
×
UNCOV
179
                Hash:  chainhash.Hash(hash),
×
UNCOV
180
                Index: 0,
×
UNCOV
181
        }
×
UNCOV
182
        fundingTxIn := wire.NewTxIn(prevOut, nil, nil)
×
UNCOV
183

×
UNCOV
184
        aliceCfg := channeldb.ChannelConfig{
×
UNCOV
185
                ChannelStateBounds: aliceBounds,
×
UNCOV
186
                CommitmentParams:   aliceCommitParams,
×
UNCOV
187
                MultiSigKey: keychain.KeyDescriptor{
×
UNCOV
188
                        PubKey: aliceKeyPub,
×
UNCOV
189
                },
×
UNCOV
190
                RevocationBasePoint: keychain.KeyDescriptor{
×
UNCOV
191
                        PubKey: aliceKeyPub,
×
UNCOV
192
                },
×
UNCOV
193
                PaymentBasePoint: keychain.KeyDescriptor{
×
UNCOV
194
                        PubKey: aliceKeyPub,
×
UNCOV
195
                },
×
UNCOV
196
                DelayBasePoint: keychain.KeyDescriptor{
×
UNCOV
197
                        PubKey: aliceKeyPub,
×
UNCOV
198
                },
×
UNCOV
199
                HtlcBasePoint: keychain.KeyDescriptor{
×
UNCOV
200
                        PubKey: aliceKeyPub,
×
UNCOV
201
                },
×
UNCOV
202
        }
×
UNCOV
203
        bobCfg := channeldb.ChannelConfig{
×
UNCOV
204
                ChannelStateBounds: bobBounds,
×
UNCOV
205
                CommitmentParams:   bobCommitParams,
×
UNCOV
206
                MultiSigKey: keychain.KeyDescriptor{
×
UNCOV
207
                        PubKey: bobKeyPub,
×
UNCOV
208
                },
×
UNCOV
209
                RevocationBasePoint: keychain.KeyDescriptor{
×
UNCOV
210
                        PubKey: bobKeyPub,
×
UNCOV
211
                },
×
UNCOV
212
                PaymentBasePoint: keychain.KeyDescriptor{
×
UNCOV
213
                        PubKey: bobKeyPub,
×
UNCOV
214
                },
×
UNCOV
215
                DelayBasePoint: keychain.KeyDescriptor{
×
UNCOV
216
                        PubKey: bobKeyPub,
×
UNCOV
217
                },
×
UNCOV
218
                HtlcBasePoint: keychain.KeyDescriptor{
×
UNCOV
219
                        PubKey: bobKeyPub,
×
UNCOV
220
                },
×
UNCOV
221
        }
×
UNCOV
222

×
UNCOV
223
        bobRoot, err := chainhash.NewHash(bobKeyPriv.Serialize())
×
UNCOV
224
        if err != nil {
×
225
                return nil, nil, err
×
226
        }
×
UNCOV
227
        bobPreimageProducer := shachain.NewRevocationProducer(*bobRoot)
×
UNCOV
228
        bobFirstRevoke, err := bobPreimageProducer.AtIndex(0)
×
UNCOV
229
        if err != nil {
×
230
                return nil, nil, err
×
231
        }
×
UNCOV
232
        bobCommitPoint := input.ComputeCommitmentPoint(bobFirstRevoke[:])
×
UNCOV
233

×
UNCOV
234
        aliceRoot, err := chainhash.NewHash(aliceKeyPriv.Serialize())
×
UNCOV
235
        if err != nil {
×
236
                return nil, nil, err
×
237
        }
×
UNCOV
238
        alicePreimageProducer := shachain.NewRevocationProducer(*aliceRoot)
×
UNCOV
239
        aliceFirstRevoke, err := alicePreimageProducer.AtIndex(0)
×
UNCOV
240
        if err != nil {
×
241
                return nil, nil, err
×
242
        }
×
UNCOV
243
        aliceCommitPoint := input.ComputeCommitmentPoint(aliceFirstRevoke[:])
×
UNCOV
244

×
UNCOV
245
        aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
×
UNCOV
246
                aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint,
×
UNCOV
247
                bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
×
UNCOV
248
                isAliceInitiator, 0,
×
UNCOV
249
        )
×
UNCOV
250
        if err != nil {
×
251
                return nil, nil, err
×
252
        }
×
253

UNCOV
254
        dbAlice, err := channeldb.Open(t.TempDir())
×
UNCOV
255
        if err != nil {
×
256
                return nil, nil, err
×
257
        }
×
UNCOV
258
        t.Cleanup(func() {
×
UNCOV
259
                require.NoError(t, dbAlice.Close())
×
UNCOV
260
        })
×
261

UNCOV
262
        dbBob, err := channeldb.Open(t.TempDir())
×
UNCOV
263
        if err != nil {
×
264
                return nil, nil, err
×
265
        }
×
UNCOV
266
        t.Cleanup(func() {
×
UNCOV
267
                require.NoError(t, dbBob.Close())
×
UNCOV
268
        })
×
269

UNCOV
270
        estimator := chainfee.NewStaticEstimator(6000, 0)
×
UNCOV
271
        feePerKw, err := estimator.EstimateFeePerKW(1)
×
UNCOV
272
        if err != nil {
×
273
                return nil, nil, err
×
274
        }
×
UNCOV
275
        commitFee := feePerKw.FeeForWeight(724)
×
UNCOV
276

×
UNCOV
277
        const broadcastHeight = 1
×
UNCOV
278
        bobAddr := &net.TCPAddr{
×
UNCOV
279
                IP:   net.ParseIP("127.0.0.1"),
×
UNCOV
280
                Port: 18555,
×
UNCOV
281
        }
×
UNCOV
282

×
UNCOV
283
        aliceAddr := &net.TCPAddr{
×
UNCOV
284
                IP:   net.ParseIP("127.0.0.1"),
×
UNCOV
285
                Port: 18556,
×
UNCOV
286
        }
×
UNCOV
287

×
UNCOV
288
        aliceCommit := channeldb.ChannelCommitment{
×
UNCOV
289
                CommitHeight:  0,
×
UNCOV
290
                LocalBalance:  lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
×
UNCOV
291
                RemoteBalance: lnwire.NewMSatFromSatoshis(bobAmount),
×
UNCOV
292
                CommitFee:     commitFee,
×
UNCOV
293
                FeePerKw:      btcutil.Amount(feePerKw),
×
UNCOV
294
                CommitTx:      aliceCommitTx,
×
UNCOV
295
                CommitSig:     bytes.Repeat([]byte{1}, 71),
×
UNCOV
296
        }
×
UNCOV
297
        bobCommit := channeldb.ChannelCommitment{
×
UNCOV
298
                CommitHeight:  0,
×
UNCOV
299
                LocalBalance:  lnwire.NewMSatFromSatoshis(bobAmount),
×
UNCOV
300
                RemoteBalance: lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
×
UNCOV
301
                CommitFee:     commitFee,
×
UNCOV
302
                FeePerKw:      btcutil.Amount(feePerKw),
×
UNCOV
303
                CommitTx:      bobCommitTx,
×
UNCOV
304
                CommitSig:     bytes.Repeat([]byte{1}, 71),
×
UNCOV
305
        }
×
UNCOV
306

×
UNCOV
307
        aliceChannelState := &channeldb.OpenChannel{
×
UNCOV
308
                LocalChanCfg:            aliceCfg,
×
UNCOV
309
                RemoteChanCfg:           bobCfg,
×
UNCOV
310
                IdentityPub:             aliceKeyPub,
×
UNCOV
311
                FundingOutpoint:         *prevOut,
×
UNCOV
312
                ChanType:                channeldb.SingleFunderTweaklessBit,
×
UNCOV
313
                IsInitiator:             isAliceInitiator,
×
UNCOV
314
                Capacity:                channelCapacity,
×
UNCOV
315
                RemoteCurrentRevocation: bobCommitPoint,
×
UNCOV
316
                RevocationProducer:      alicePreimageProducer,
×
UNCOV
317
                RevocationStore:         shachain.NewRevocationStore(),
×
UNCOV
318
                LocalCommitment:         aliceCommit,
×
UNCOV
319
                RemoteCommitment:        aliceCommit,
×
UNCOV
320
                ShortChannelID:          chanID,
×
UNCOV
321
                Db:                      dbAlice.ChannelStateDB(),
×
UNCOV
322
                Packager:                channeldb.NewChannelPackager(chanID),
×
UNCOV
323
                FundingTxn:              channels.TestFundingTx,
×
UNCOV
324
        }
×
UNCOV
325

×
UNCOV
326
        bobChannelState := &channeldb.OpenChannel{
×
UNCOV
327
                LocalChanCfg:            bobCfg,
×
UNCOV
328
                RemoteChanCfg:           aliceCfg,
×
UNCOV
329
                IdentityPub:             bobKeyPub,
×
UNCOV
330
                FundingOutpoint:         *prevOut,
×
UNCOV
331
                ChanType:                channeldb.SingleFunderTweaklessBit,
×
UNCOV
332
                IsInitiator:             !isAliceInitiator,
×
UNCOV
333
                Capacity:                channelCapacity,
×
UNCOV
334
                RemoteCurrentRevocation: aliceCommitPoint,
×
UNCOV
335
                RevocationProducer:      bobPreimageProducer,
×
UNCOV
336
                RevocationStore:         shachain.NewRevocationStore(),
×
UNCOV
337
                LocalCommitment:         bobCommit,
×
UNCOV
338
                RemoteCommitment:        bobCommit,
×
UNCOV
339
                ShortChannelID:          chanID,
×
UNCOV
340
                Db:                      dbBob.ChannelStateDB(),
×
UNCOV
341
                Packager:                channeldb.NewChannelPackager(chanID),
×
UNCOV
342
        }
×
UNCOV
343

×
UNCOV
344
        if err := aliceChannelState.SyncPending(bobAddr, broadcastHeight); err != nil {
×
345
                return nil, nil, err
×
346
        }
×
347

UNCOV
348
        if err := bobChannelState.SyncPending(aliceAddr, broadcastHeight); err != nil {
×
349
                return nil, nil, err
×
350
        }
×
351

UNCOV
352
        aliceSigner := input.NewMockSigner(
×
UNCOV
353
                []*btcec.PrivateKey{aliceKeyPriv}, nil,
×
UNCOV
354
        )
×
UNCOV
355
        bobSigner := input.NewMockSigner(
×
UNCOV
356
                []*btcec.PrivateKey{bobKeyPriv}, nil,
×
UNCOV
357
        )
×
UNCOV
358

×
UNCOV
359
        alicePool := lnwallet.NewSigPool(runtime.NumCPU(), aliceSigner)
×
UNCOV
360
        signerMock := lnwallet.NewDefaultAuxSignerMock(t)
×
UNCOV
361
        channelAlice, err := lnwallet.NewLightningChannel(
×
UNCOV
362
                aliceSigner, aliceChannelState, alicePool,
×
UNCOV
363
                lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}),
×
UNCOV
364
                lnwallet.WithAuxSigner(signerMock),
×
UNCOV
365
        )
×
UNCOV
366
        if err != nil {
×
367
                return nil, nil, err
×
368
        }
×
UNCOV
369
        alicePool.Start()
×
UNCOV
370

×
UNCOV
371
        bobPool := lnwallet.NewSigPool(runtime.NumCPU(), bobSigner)
×
UNCOV
372
        channelBob, err := lnwallet.NewLightningChannel(
×
UNCOV
373
                bobSigner, bobChannelState, bobPool,
×
UNCOV
374
                lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}),
×
UNCOV
375
                lnwallet.WithAuxSigner(signerMock),
×
UNCOV
376
        )
×
UNCOV
377
        if err != nil {
×
378
                return nil, nil, err
×
379
        }
×
UNCOV
380
        bobPool.Start()
×
UNCOV
381

×
UNCOV
382
        // Now that the channel are open, simulate the start of a session by
×
UNCOV
383
        // having Alice and Bob extend their revocation windows to each other.
×
UNCOV
384
        aliceNextRevoke, err := channelAlice.NextRevocationKey()
×
UNCOV
385
        if err != nil {
×
386
                return nil, nil, err
×
387
        }
×
UNCOV
388
        if err := channelBob.InitNextRevocation(aliceNextRevoke); err != nil {
×
389
                return nil, nil, err
×
390
        }
×
391

UNCOV
392
        bobNextRevoke, err := channelBob.NextRevocationKey()
×
UNCOV
393
        if err != nil {
×
394
                return nil, nil, err
×
395
        }
×
UNCOV
396
        if err := channelAlice.InitNextRevocation(bobNextRevoke); err != nil {
×
397
                return nil, nil, err
×
398
        }
×
399

UNCOV
400
        restoreAlice := func() (*lnwallet.LightningChannel, error) {
×
UNCOV
401
                aliceStoredChannels, err := dbAlice.ChannelStateDB().
×
UNCOV
402
                        FetchOpenChannels(aliceKeyPub)
×
UNCOV
403
                switch err {
×
UNCOV
404
                case nil:
×
405
                case kvdb.ErrDatabaseNotOpen:
×
406
                        dbAlice, err = channeldb.Open(dbAlice.Path())
×
407
                        if err != nil {
×
408
                                return nil, errors.Errorf("unable to reopen alice "+
×
409
                                        "db: %v", err)
×
410
                        }
×
411

412
                        aliceStoredChannels, err = dbAlice.ChannelStateDB().
×
413
                                FetchOpenChannels(aliceKeyPub)
×
414
                        if err != nil {
×
415
                                return nil, errors.Errorf("unable to fetch alice "+
×
416
                                        "channel: %v", err)
×
417
                        }
×
418
                default:
×
419
                        return nil, errors.Errorf("unable to fetch alice channel: "+
×
420
                                "%v", err)
×
421
                }
422

UNCOV
423
                var aliceStoredChannel *channeldb.OpenChannel
×
UNCOV
424
                for _, channel := range aliceStoredChannels {
×
UNCOV
425
                        if channel.FundingOutpoint.String() == prevOut.String() {
×
UNCOV
426
                                aliceStoredChannel = channel
×
UNCOV
427
                                break
×
428
                        }
429
                }
430

UNCOV
431
                if aliceStoredChannel == nil {
×
432
                        return nil, errors.New("unable to find stored alice channel")
×
433
                }
×
434

UNCOV
435
                newAliceChannel, err := lnwallet.NewLightningChannel(
×
UNCOV
436
                        aliceSigner, aliceStoredChannel, alicePool,
×
UNCOV
437
                        lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}),
×
UNCOV
438
                        lnwallet.WithAuxSigner(signerMock),
×
UNCOV
439
                )
×
UNCOV
440
                if err != nil {
×
441
                        return nil, errors.Errorf("unable to create new channel: %v",
×
442
                                err)
×
443
                }
×
444

UNCOV
445
                return newAliceChannel, nil
×
446
        }
447

UNCOV
448
        restoreBob := func() (*lnwallet.LightningChannel, error) {
×
UNCOV
449
                bobStoredChannels, err := dbBob.ChannelStateDB().
×
UNCOV
450
                        FetchOpenChannels(bobKeyPub)
×
UNCOV
451
                switch err {
×
UNCOV
452
                case nil:
×
453
                case kvdb.ErrDatabaseNotOpen:
×
454
                        dbBob, err = channeldb.Open(dbBob.Path())
×
455
                        if err != nil {
×
456
                                return nil, errors.Errorf("unable to reopen bob "+
×
457
                                        "db: %v", err)
×
458
                        }
×
459

460
                        bobStoredChannels, err = dbBob.ChannelStateDB().
×
461
                                FetchOpenChannels(bobKeyPub)
×
462
                        if err != nil {
×
463
                                return nil, errors.Errorf("unable to fetch bob "+
×
464
                                        "channel: %v", err)
×
465
                        }
×
466
                default:
×
467
                        return nil, errors.Errorf("unable to fetch bob channel: "+
×
468
                                "%v", err)
×
469
                }
470

UNCOV
471
                var bobStoredChannel *channeldb.OpenChannel
×
UNCOV
472
                for _, channel := range bobStoredChannels {
×
UNCOV
473
                        if channel.FundingOutpoint.String() == prevOut.String() {
×
UNCOV
474
                                bobStoredChannel = channel
×
UNCOV
475
                                break
×
476
                        }
477
                }
478

UNCOV
479
                if bobStoredChannel == nil {
×
480
                        return nil, errors.New("unable to find stored bob channel")
×
481
                }
×
482

UNCOV
483
                newBobChannel, err := lnwallet.NewLightningChannel(
×
UNCOV
484
                        bobSigner, bobStoredChannel, bobPool,
×
UNCOV
485
                        lnwallet.WithLeafStore(&lnwallet.MockAuxLeafStore{}),
×
UNCOV
486
                        lnwallet.WithAuxSigner(signerMock),
×
UNCOV
487
                )
×
UNCOV
488
                if err != nil {
×
489
                        return nil, errors.Errorf("unable to create new channel: %v",
×
490
                                err)
×
491
                }
×
UNCOV
492
                return newBobChannel, nil
×
493
        }
494

UNCOV
495
        testLightningChannelAlice := &testLightningChannel{
×
UNCOV
496
                channel: channelAlice,
×
UNCOV
497
                restore: restoreAlice,
×
UNCOV
498
        }
×
UNCOV
499

×
UNCOV
500
        testLightningChannelBob := &testLightningChannel{
×
UNCOV
501
                channel: channelBob,
×
UNCOV
502
                restore: restoreBob,
×
UNCOV
503
        }
×
UNCOV
504

×
UNCOV
505
        return testLightningChannelAlice, testLightningChannelBob, nil
×
506
}
507

508
// getChanID retrieves the channel point from an lnnwire message.
UNCOV
509
func getChanID(msg lnwire.Message) (lnwire.ChannelID, error) {
×
UNCOV
510
        var chanID lnwire.ChannelID
×
UNCOV
511
        switch msg := msg.(type) {
×
UNCOV
512
        case *lnwire.UpdateAddHTLC:
×
UNCOV
513
                chanID = msg.ChanID
×
UNCOV
514
        case *lnwire.UpdateFulfillHTLC:
×
UNCOV
515
                chanID = msg.ChanID
×
516
        case *lnwire.UpdateFailHTLC:
×
517
                chanID = msg.ChanID
×
UNCOV
518
        case *lnwire.RevokeAndAck:
×
UNCOV
519
                chanID = msg.ChanID
×
UNCOV
520
        case *lnwire.CommitSig:
×
UNCOV
521
                chanID = msg.ChanID
×
UNCOV
522
        case *lnwire.ChannelReestablish:
×
UNCOV
523
                chanID = msg.ChanID
×
UNCOV
524
        case *lnwire.ChannelReady:
×
UNCOV
525
                chanID = msg.ChanID
×
UNCOV
526
        case *lnwire.UpdateFee:
×
UNCOV
527
                chanID = msg.ChanID
×
528
        default:
×
529
                return chanID, fmt.Errorf("unknown type: %T", msg)
×
530
        }
531

UNCOV
532
        return chanID, nil
×
533
}
534

535
// generateHoldPayment generates the htlc add request by given path blob and
536
// invoice which should be added by destination peer.
537
func generatePaymentWithPreimage(invoiceAmt, htlcAmt lnwire.MilliSatoshi,
538
        timelock uint32, blob [lnwire.OnionPacketSize]byte,
539
        preimage *lntypes.Preimage, rhash, payAddr [32]byte) (
UNCOV
540
        *invoices.Invoice, *lnwire.UpdateAddHTLC, uint64, error) {
×
UNCOV
541

×
UNCOV
542
        // Create the db invoice. Normally the payment requests needs to be set,
×
UNCOV
543
        // because it is decoded in InvoiceRegistry to obtain the cltv expiry.
×
UNCOV
544
        // But because the mock registry used in tests is mocking the decode
×
UNCOV
545
        // step and always returning the value of testInvoiceCltvExpiry, we
×
UNCOV
546
        // don't need to bother here with creating and signing a payment
×
UNCOV
547
        // request.
×
UNCOV
548

×
UNCOV
549
        invoice := &invoices.Invoice{
×
UNCOV
550
                CreationDate: time.Now(),
×
UNCOV
551
                Terms: invoices.ContractTerm{
×
UNCOV
552
                        FinalCltvDelta:  testInvoiceCltvExpiry,
×
UNCOV
553
                        Value:           invoiceAmt,
×
UNCOV
554
                        PaymentPreimage: preimage,
×
UNCOV
555
                        PaymentAddr:     payAddr,
×
UNCOV
556
                        Features: lnwire.NewFeatureVector(
×
UNCOV
557
                                nil, lnwire.Features,
×
UNCOV
558
                        ),
×
UNCOV
559
                },
×
UNCOV
560
                HodlInvoice: preimage == nil,
×
UNCOV
561
        }
×
UNCOV
562

×
UNCOV
563
        htlc := &lnwire.UpdateAddHTLC{
×
UNCOV
564
                PaymentHash: rhash,
×
UNCOV
565
                Amount:      htlcAmt,
×
UNCOV
566
                Expiry:      timelock,
×
UNCOV
567
                OnionBlob:   blob,
×
UNCOV
568
        }
×
UNCOV
569

×
UNCOV
570
        pid, err := generateRandomBytes(8)
×
UNCOV
571
        if err != nil {
×
572
                return nil, nil, 0, err
×
573
        }
×
UNCOV
574
        paymentID := binary.BigEndian.Uint64(pid)
×
UNCOV
575

×
UNCOV
576
        return invoice, htlc, paymentID, nil
×
577
}
578

579
// generatePayment generates the htlc add request by given path blob and
580
// invoice which should be added by destination peer.
581
func generatePayment(invoiceAmt, htlcAmt lnwire.MilliSatoshi, timelock uint32,
582
        blob [lnwire.OnionPacketSize]byte) (*invoices.Invoice,
UNCOV
583
        *lnwire.UpdateAddHTLC, uint64, error) {
×
UNCOV
584

×
UNCOV
585
        var preimage lntypes.Preimage
×
UNCOV
586
        r, err := generateRandomBytes(sha256.Size)
×
UNCOV
587
        if err != nil {
×
588
                return nil, nil, 0, err
×
589
        }
×
UNCOV
590
        copy(preimage[:], r)
×
UNCOV
591

×
UNCOV
592
        rhash := sha256.Sum256(preimage[:])
×
UNCOV
593

×
UNCOV
594
        var payAddr [sha256.Size]byte
×
UNCOV
595
        r, err = generateRandomBytes(sha256.Size)
×
UNCOV
596
        if err != nil {
×
597
                return nil, nil, 0, err
×
598
        }
×
UNCOV
599
        copy(payAddr[:], r)
×
UNCOV
600

×
UNCOV
601
        return generatePaymentWithPreimage(
×
UNCOV
602
                invoiceAmt, htlcAmt, timelock, blob, &preimage, rhash, payAddr,
×
UNCOV
603
        )
×
604
}
605

606
// generateRoute generates the path blob by given array of peers.
607
func generateRoute(hops ...*hop.Payload) (
UNCOV
608
        [lnwire.OnionPacketSize]byte, error) {
×
UNCOV
609

×
UNCOV
610
        var blob [lnwire.OnionPacketSize]byte
×
UNCOV
611
        if len(hops) == 0 {
×
612
                return blob, errors.New("empty path")
×
613
        }
×
614

UNCOV
615
        iterator := newMockHopIterator(hops...)
×
UNCOV
616

×
UNCOV
617
        w := bytes.NewBuffer(blob[0:0])
×
UNCOV
618
        if err := iterator.EncodeNextHop(w); err != nil {
×
619
                return blob, err
×
620
        }
×
621

UNCOV
622
        return blob, nil
×
623

624
}
625

626
// threeHopNetwork is used for managing the created cluster of 3 hops.
627
type threeHopNetwork struct {
628
        aliceServer       *mockServer
629
        aliceChannelLink  *channelLink
630
        aliceOnionDecoder *mockIteratorDecoder
631

632
        bobServer            *mockServer
633
        firstBobChannelLink  *channelLink
634
        secondBobChannelLink *channelLink
635
        bobOnionDecoder      *mockIteratorDecoder
636

637
        carolServer       *mockServer
638
        carolChannelLink  *channelLink
639
        carolOnionDecoder *mockIteratorDecoder
640

641
        hopNetwork
642
}
643

644
// generateHops creates the per hop payload, the total amount to be sent, and
645
// also the time lock value needed to route an HTLC with the target amount over
646
// the specified path.
647
func generateHops(payAmt lnwire.MilliSatoshi, startingHeight uint32,
UNCOV
648
        path ...*channelLink) (lnwire.MilliSatoshi, uint32, []*hop.Payload) {
×
UNCOV
649

×
UNCOV
650
        totalTimelock := startingHeight
×
UNCOV
651
        runningAmt := payAmt
×
UNCOV
652

×
UNCOV
653
        hops := make([]*hop.Payload, len(path))
×
UNCOV
654
        for i := len(path) - 1; i >= 0; i-- {
×
UNCOV
655
                // If this is the last hop, then the next hop is the special
×
UNCOV
656
                // "exit node". Otherwise, we look to the "prior" hop.
×
UNCOV
657
                nextHop := hop.Exit
×
UNCOV
658
                if i != len(path)-1 {
×
UNCOV
659
                        nextHop = path[i+1].channel.ShortChanID()
×
UNCOV
660
                }
×
661

UNCOV
662
                var timeLock uint32
×
UNCOV
663
                // If this is the last, hop, then the time lock will be their
×
UNCOV
664
                // specified delta policy plus our starting height.
×
UNCOV
665
                if i == len(path)-1 {
×
UNCOV
666
                        totalTimelock += testInvoiceCltvExpiry
×
UNCOV
667
                        timeLock = totalTimelock
×
UNCOV
668
                } else {
×
UNCOV
669
                        // Otherwise, the outgoing time lock should be the
×
UNCOV
670
                        // incoming timelock minus their specified delta.
×
UNCOV
671
                        delta := path[i+1].cfg.FwrdingPolicy.TimeLockDelta
×
UNCOV
672
                        totalTimelock += delta
×
UNCOV
673
                        timeLock = totalTimelock - delta
×
UNCOV
674
                }
×
675

676
                // Finally, we'll need to calculate the amount to forward. For
677
                // the last hop, it's just the payment amount.
UNCOV
678
                amount := payAmt
×
UNCOV
679
                if i != len(path)-1 {
×
UNCOV
680
                        prevHop := hops[i+1]
×
UNCOV
681
                        prevAmount := prevHop.ForwardingInfo().AmountToForward
×
UNCOV
682

×
UNCOV
683
                        fee := ExpectedFee(path[i].cfg.FwrdingPolicy, prevAmount)
×
UNCOV
684
                        runningAmt += fee
×
UNCOV
685

×
UNCOV
686
                        // Otherwise, for a node to forward an HTLC, then
×
UNCOV
687
                        // following inequality most hold true:
×
UNCOV
688
                        //     * amt_in - fee >= amt_to_forward
×
UNCOV
689
                        amount = runningAmt - fee
×
UNCOV
690
                }
×
691

UNCOV
692
                var nextHopBytes [8]byte
×
UNCOV
693
                binary.BigEndian.PutUint64(nextHopBytes[:], nextHop.ToUint64())
×
UNCOV
694

×
UNCOV
695
                hops[i] = hop.NewLegacyPayload(&sphinx.HopData{
×
UNCOV
696
                        Realm:         [1]byte{}, // hop.BitcoinNetwork
×
UNCOV
697
                        NextAddress:   nextHopBytes,
×
UNCOV
698
                        ForwardAmount: uint64(amount),
×
UNCOV
699
                        OutgoingCltv:  timeLock,
×
UNCOV
700
                })
×
701
        }
702

UNCOV
703
        return runningAmt, totalTimelock, hops
×
704
}
705

706
type paymentResponse struct {
707
        rhash lntypes.Hash
708
        err   chan error
709
}
710

UNCOV
711
func (r *paymentResponse) Wait(d time.Duration) (lntypes.Hash, error) {
×
UNCOV
712
        return r.rhash, waitForPaymentResult(r.err, d)
×
UNCOV
713
}
×
714

715
// waitForPaymentResult waits for either an error to be received on c or a
716
// timeout.
UNCOV
717
func waitForPaymentResult(c chan error, d time.Duration) error {
×
UNCOV
718
        select {
×
UNCOV
719
        case err := <-c:
×
UNCOV
720
                close(c)
×
UNCOV
721
                return err
×
UNCOV
722
        case <-time.After(d):
×
UNCOV
723
                return errors.New("htlc was not settled in time")
×
724
        }
725
}
726

727
// waitForPayFuncResult executes the given function and waits for a result with
728
// a timeout.
UNCOV
729
func waitForPayFuncResult(payFunc func() error, d time.Duration) error {
×
UNCOV
730
        errChan := make(chan error)
×
UNCOV
731
        go func() {
×
UNCOV
732
                errChan <- payFunc()
×
UNCOV
733
        }()
×
734

UNCOV
735
        return waitForPaymentResult(errChan, d)
×
736
}
737

738
// makePayment takes the destination node and amount as input, sends the
739
// payment and returns the error channel to wait for error to be received and
740
// invoice in order to check its status after the payment finished.
741
//
742
// With this function you can send payments:
743
// * from Alice to Bob
744
// * from Alice to Carol through the Bob
745
// * from Alice to some another peer through the Bob
746
func makePayment(sendingPeer, receivingPeer lnpeer.Peer,
747
        firstHop lnwire.ShortChannelID, hops []*hop.Payload,
748
        invoiceAmt, htlcAmt lnwire.MilliSatoshi,
UNCOV
749
        timelock uint32) *paymentResponse {
×
UNCOV
750

×
UNCOV
751
        paymentErr := make(chan error, 1)
×
UNCOV
752
        var rhash lntypes.Hash
×
UNCOV
753

×
UNCOV
754
        invoice, payFunc, err := preparePayment(sendingPeer, receivingPeer,
×
UNCOV
755
                firstHop, hops, invoiceAmt, htlcAmt, timelock,
×
UNCOV
756
        )
×
UNCOV
757
        if err != nil {
×
758
                paymentErr <- err
×
759
                return &paymentResponse{
×
760
                        rhash: rhash,
×
761
                        err:   paymentErr,
×
762
                }
×
763
        }
×
764

UNCOV
765
        rhash = invoice.Terms.PaymentPreimage.Hash()
×
UNCOV
766

×
UNCOV
767
        // Send payment and expose err channel.
×
UNCOV
768
        go func() {
×
UNCOV
769
                paymentErr <- payFunc()
×
UNCOV
770
        }()
×
771

UNCOV
772
        return &paymentResponse{
×
UNCOV
773
                rhash: rhash,
×
UNCOV
774
                err:   paymentErr,
×
UNCOV
775
        }
×
776
}
777

778
// preparePayment creates an invoice at the receivingPeer and returns a function
779
// that, when called, launches the payment from the sendingPeer.
780
func preparePayment(sendingPeer, receivingPeer lnpeer.Peer,
781
        firstHop lnwire.ShortChannelID, hops []*hop.Payload,
782
        invoiceAmt, htlcAmt lnwire.MilliSatoshi,
UNCOV
783
        timelock uint32) (*invoices.Invoice, func() error, error) {
×
UNCOV
784

×
UNCOV
785
        sender := sendingPeer.(*mockServer)
×
UNCOV
786
        receiver := receivingPeer.(*mockServer)
×
UNCOV
787

×
UNCOV
788
        // Generate route convert it to blob, and return next destination for
×
UNCOV
789
        // htlc add request.
×
UNCOV
790
        blob, err := generateRoute(hops...)
×
UNCOV
791
        if err != nil {
×
792
                return nil, nil, err
×
793
        }
×
794

795
        // Generate payment: invoice and htlc.
UNCOV
796
        invoice, htlc, pid, err := generatePayment(
×
UNCOV
797
                invoiceAmt, htlcAmt, timelock, blob,
×
UNCOV
798
        )
×
UNCOV
799
        if err != nil {
×
800
                return nil, nil, err
×
801
        }
×
802

803
        // Check who is last in the route and add invoice to server registry.
UNCOV
804
        hash := invoice.Terms.PaymentPreimage.Hash()
×
UNCOV
805
        if err := receiver.registry.AddInvoice(
×
UNCOV
806
                context.Background(), *invoice, hash,
×
UNCOV
807
        ); err != nil {
×
808
                return nil, nil, err
×
809
        }
×
810

811
        // Send payment and expose err channel.
UNCOV
812
        return invoice, func() error {
×
UNCOV
813
                err := sender.htlcSwitch.SendHTLC(
×
UNCOV
814
                        firstHop, pid, htlc,
×
UNCOV
815
                )
×
UNCOV
816
                if err != nil {
×
UNCOV
817
                        return err
×
UNCOV
818
                }
×
UNCOV
819
                resultChan, err := sender.htlcSwitch.GetAttemptResult(
×
UNCOV
820
                        pid, hash, newMockDeobfuscator(),
×
UNCOV
821
                )
×
UNCOV
822
                if err != nil {
×
823
                        return err
×
824
                }
×
825

UNCOV
826
                result, ok := <-resultChan
×
UNCOV
827
                if !ok {
×
UNCOV
828
                        return fmt.Errorf("shutting down")
×
UNCOV
829
                }
×
830

UNCOV
831
                if result.Error != nil {
×
UNCOV
832
                        return result.Error
×
UNCOV
833
                }
×
834

UNCOV
835
                return nil
×
836
        }, nil
837
}
838

839
// start starts the three hop network alice,bob,carol servers.
UNCOV
840
func (n *threeHopNetwork) start() error {
×
UNCOV
841
        if err := n.aliceServer.Start(); err != nil {
×
842
                return err
×
843
        }
×
UNCOV
844
        if err := n.bobServer.Start(); err != nil {
×
845
                return err
×
846
        }
×
UNCOV
847
        if err := n.carolServer.Start(); err != nil {
×
848
                return err
×
849
        }
×
850

UNCOV
851
        return waitLinksEligible(map[string]*channelLink{
×
UNCOV
852
                "alice":      n.aliceChannelLink,
×
UNCOV
853
                "bob first":  n.firstBobChannelLink,
×
UNCOV
854
                "bob second": n.secondBobChannelLink,
×
UNCOV
855
                "carol":      n.carolChannelLink,
×
UNCOV
856
        })
×
857
}
858

859
// stop stops nodes and cleanup its databases.
UNCOV
860
func (n *threeHopNetwork) stop() {
×
UNCOV
861
        done := make(chan struct{})
×
UNCOV
862
        go func() {
×
UNCOV
863
                n.aliceServer.Stop()
×
UNCOV
864
                done <- struct{}{}
×
UNCOV
865
        }()
×
866

UNCOV
867
        go func() {
×
UNCOV
868
                n.bobServer.Stop()
×
UNCOV
869
                done <- struct{}{}
×
UNCOV
870
        }()
×
871

UNCOV
872
        go func() {
×
UNCOV
873
                n.carolServer.Stop()
×
UNCOV
874
                done <- struct{}{}
×
UNCOV
875
        }()
×
876

UNCOV
877
        for i := 0; i < 3; i++ {
×
UNCOV
878
                <-done
×
UNCOV
879
        }
×
880
}
881

882
type clusterChannels struct {
883
        aliceToBob *lnwallet.LightningChannel
884
        bobToAlice *lnwallet.LightningChannel
885
        bobToCarol *lnwallet.LightningChannel
886
        carolToBob *lnwallet.LightningChannel
887
}
888

889
// createClusterChannels creates lightning channels which are needed for
890
// network cluster to be initialized.
891
func createClusterChannels(t *testing.T, aliceToBob, bobToCarol btcutil.Amount) (
UNCOV
892
        *clusterChannels, func() (*clusterChannels, error), error) {
×
UNCOV
893

×
UNCOV
894
        _, _, firstChanID, secondChanID := genIDs()
×
UNCOV
895

×
UNCOV
896
        // Create lightning channels between Alice<->Bob and Bob<->Carol
×
UNCOV
897
        aliceChannel, firstBobChannel, err := createTestChannel(t, alicePrivKey,
×
UNCOV
898
                bobPrivKey, aliceToBob, aliceToBob, 0, 0, firstChanID,
×
UNCOV
899
        )
×
UNCOV
900
        if err != nil {
×
901
                return nil, nil, errors.Errorf("unable to create "+
×
902
                        "alice<->bob channel: %v", err)
×
903
        }
×
904

UNCOV
905
        secondBobChannel, carolChannel, err := createTestChannel(t, bobPrivKey,
×
UNCOV
906
                carolPrivKey, bobToCarol, bobToCarol, 0, 0, secondChanID,
×
UNCOV
907
        )
×
UNCOV
908
        if err != nil {
×
909
                return nil, nil, errors.Errorf("unable to create "+
×
910
                        "bob<->carol channel: %v", err)
×
911
        }
×
912

UNCOV
913
        restoreFromDb := func() (*clusterChannels, error) {
×
UNCOV
914

×
UNCOV
915
                a2b, err := aliceChannel.restore()
×
UNCOV
916
                if err != nil {
×
917
                        return nil, err
×
918
                }
×
919

UNCOV
920
                b2a, err := firstBobChannel.restore()
×
UNCOV
921
                if err != nil {
×
922
                        return nil, err
×
923
                }
×
924

UNCOV
925
                b2c, err := secondBobChannel.restore()
×
UNCOV
926
                if err != nil {
×
927
                        return nil, err
×
928
                }
×
929

UNCOV
930
                c2b, err := carolChannel.restore()
×
UNCOV
931
                if err != nil {
×
932
                        return nil, err
×
933
                }
×
934

UNCOV
935
                return &clusterChannels{
×
UNCOV
936
                        aliceToBob: a2b,
×
UNCOV
937
                        bobToAlice: b2a,
×
UNCOV
938
                        bobToCarol: b2c,
×
UNCOV
939
                        carolToBob: c2b,
×
UNCOV
940
                }, nil
×
941
        }
942

UNCOV
943
        return &clusterChannels{
×
UNCOV
944
                aliceToBob: aliceChannel.channel,
×
UNCOV
945
                bobToAlice: firstBobChannel.channel,
×
UNCOV
946
                bobToCarol: secondBobChannel.channel,
×
UNCOV
947
                carolToBob: carolChannel.channel,
×
UNCOV
948
        }, restoreFromDb, nil
×
949
}
950

951
// newThreeHopNetwork function creates the following topology and returns the
952
// control object to manage this cluster:
953
//
954
// alice                      bob                             carol
955
// server - <-connection-> - server - - <-connection-> - - - server
956
//
957
//        |                           |                               |
958
//
959
// alice htlc                     bob htlc                          carol htlc
960
// switch                      switch        \                    switch
961
//
962
//        |                         |       \                       |
963
//        |                         |        \                       |
964
//
965
// alice                   first bob     second bob           carol
966
// channel link                      channel link   channel link      channel link
967
//
968
// This function takes server options which can be used to apply custom
969
// settings to alice, bob and carol.
970
func newThreeHopNetwork(t testing.TB, aliceChannel, firstBobChannel,
971
        secondBobChannel, carolChannel *lnwallet.LightningChannel,
UNCOV
972
        startingHeight uint32, opts ...serverOption) *threeHopNetwork {
×
UNCOV
973

×
UNCOV
974
        aliceDb := aliceChannel.State().Db.GetParentDB()
×
UNCOV
975
        bobDb := firstBobChannel.State().Db.GetParentDB()
×
UNCOV
976
        carolDb := carolChannel.State().Db.GetParentDB()
×
UNCOV
977

×
UNCOV
978
        hopNetwork := newHopNetwork()
×
UNCOV
979

×
UNCOV
980
        // Create three peers/servers.
×
UNCOV
981
        aliceServer, err := newMockServer(
×
UNCOV
982
                t, "alice", startingHeight, aliceDb, hopNetwork.defaultDelta,
×
UNCOV
983
        )
×
UNCOV
984
        require.NoError(t, err, "unable to create alice server")
×
UNCOV
985
        bobServer, err := newMockServer(
×
UNCOV
986
                t, "bob", startingHeight, bobDb, hopNetwork.defaultDelta,
×
UNCOV
987
        )
×
UNCOV
988
        require.NoError(t, err, "unable to create bob server")
×
UNCOV
989
        carolServer, err := newMockServer(
×
UNCOV
990
                t, "carol", startingHeight, carolDb, hopNetwork.defaultDelta,
×
UNCOV
991
        )
×
UNCOV
992
        require.NoError(t, err, "unable to create carol server")
×
UNCOV
993

×
UNCOV
994
        // Apply all additional functional options to the servers before
×
UNCOV
995
        // creating any links.
×
UNCOV
996
        for _, option := range opts {
×
UNCOV
997
                option(aliceServer, bobServer, carolServer)
×
UNCOV
998
        }
×
999

1000
        // Create mock decoder instead of sphinx one in order to mock the route
1001
        // which htlc should follow.
UNCOV
1002
        aliceDecoder := newMockIteratorDecoder()
×
UNCOV
1003
        bobDecoder := newMockIteratorDecoder()
×
UNCOV
1004
        carolDecoder := newMockIteratorDecoder()
×
UNCOV
1005

×
UNCOV
1006
        aliceChannelLink, err := hopNetwork.createChannelLink(aliceServer,
×
UNCOV
1007
                bobServer, aliceChannel, aliceDecoder,
×
UNCOV
1008
        )
×
UNCOV
1009
        if err != nil {
×
1010
                t.Fatal(err)
×
1011
        }
×
1012

UNCOV
1013
        firstBobChannelLink, err := hopNetwork.createChannelLink(bobServer,
×
UNCOV
1014
                aliceServer, firstBobChannel, bobDecoder)
×
UNCOV
1015
        if err != nil {
×
1016
                t.Fatal(err)
×
1017
        }
×
1018

UNCOV
1019
        secondBobChannelLink, err := hopNetwork.createChannelLink(bobServer,
×
UNCOV
1020
                carolServer, secondBobChannel, bobDecoder)
×
UNCOV
1021
        if err != nil {
×
1022
                t.Fatal(err)
×
1023
        }
×
1024

UNCOV
1025
        carolChannelLink, err := hopNetwork.createChannelLink(carolServer,
×
UNCOV
1026
                bobServer, carolChannel, carolDecoder)
×
UNCOV
1027
        if err != nil {
×
1028
                t.Fatal(err)
×
1029
        }
×
1030

UNCOV
1031
        return &threeHopNetwork{
×
UNCOV
1032
                aliceServer:       aliceServer,
×
UNCOV
1033
                aliceChannelLink:  aliceChannelLink.(*channelLink),
×
UNCOV
1034
                aliceOnionDecoder: aliceDecoder,
×
UNCOV
1035

×
UNCOV
1036
                bobServer:            bobServer,
×
UNCOV
1037
                firstBobChannelLink:  firstBobChannelLink.(*channelLink),
×
UNCOV
1038
                secondBobChannelLink: secondBobChannelLink.(*channelLink),
×
UNCOV
1039
                bobOnionDecoder:      bobDecoder,
×
UNCOV
1040

×
UNCOV
1041
                carolServer:       carolServer,
×
UNCOV
1042
                carolChannelLink:  carolChannelLink.(*channelLink),
×
UNCOV
1043
                carolOnionDecoder: carolDecoder,
×
UNCOV
1044

×
UNCOV
1045
                hopNetwork: *hopNetwork,
×
UNCOV
1046
        }
×
1047
}
1048

1049
// serverOption is a function which alters the three servers created for
1050
// a three hop network to allow custom settings on each server.
1051
type serverOption func(aliceServer, bobServer, carolServer *mockServer)
1052

1053
// serverOptionWithHtlcNotifier is a functional option for the creation of
1054
// three hop network servers which allows setting of htlc notifiers.
1055
// Note that these notifiers should be started and stopped by the calling
1056
// function.
1057
func serverOptionWithHtlcNotifier(alice, bob,
UNCOV
1058
        carol *HtlcNotifier) serverOption {
×
UNCOV
1059

×
UNCOV
1060
        return func(aliceServer, bobServer, carolServer *mockServer) {
×
UNCOV
1061
                aliceServer.htlcSwitch.cfg.HtlcNotifier = alice
×
UNCOV
1062
                bobServer.htlcSwitch.cfg.HtlcNotifier = bob
×
UNCOV
1063
                carolServer.htlcSwitch.cfg.HtlcNotifier = carol
×
UNCOV
1064
        }
×
1065
}
1066

1067
// serverOptionRejectHtlc is the functional option for setting the reject
1068
// htlc config option in each server's switch.
UNCOV
1069
func serverOptionRejectHtlc(alice, bob, carol bool) serverOption {
×
UNCOV
1070
        return func(aliceServer, bobServer, carolServer *mockServer) {
×
UNCOV
1071
                aliceServer.htlcSwitch.cfg.RejectHTLC = alice
×
UNCOV
1072
                bobServer.htlcSwitch.cfg.RejectHTLC = bob
×
UNCOV
1073
                carolServer.htlcSwitch.cfg.RejectHTLC = carol
×
UNCOV
1074
        }
×
1075
}
1076

1077
// createMirroredChannel creates two LightningChannel objects which represent
1078
// the state machines on either side of a single channel between alice and bob.
1079
func createMirroredChannel(t *testing.T, aliceToBob,
1080
        bobToAlice btcutil.Amount) (*testLightningChannel,
UNCOV
1081
        *testLightningChannel, error) {
×
UNCOV
1082

×
UNCOV
1083
        _, _, firstChanID, _ := genIDs()
×
UNCOV
1084

×
UNCOV
1085
        // Create lightning channels between Alice<->Bob for Alice and Bob
×
UNCOV
1086
        alice, bob, err := createTestChannel(t, alicePrivKey, bobPrivKey,
×
UNCOV
1087
                aliceToBob, bobToAlice, 0, 0, firstChanID,
×
UNCOV
1088
        )
×
UNCOV
1089
        if err != nil {
×
1090
                return nil, nil, errors.Errorf("unable to create "+
×
1091
                        "alice<->bob channel: %v", err)
×
1092
        }
×
1093

UNCOV
1094
        return alice, bob, nil
×
1095
}
1096

1097
// hopNetwork is the base struct for two and three hop networks
1098
type hopNetwork struct {
1099
        feeEstimator *mockFeeEstimator
1100
        globalPolicy models.ForwardingPolicy
1101
        obfuscator   hop.ErrorEncrypter
1102

1103
        defaultDelta uint32
1104
}
1105

UNCOV
1106
func newHopNetwork() *hopNetwork {
×
UNCOV
1107
        defaultDelta := uint32(6)
×
UNCOV
1108

×
UNCOV
1109
        globalPolicy := models.ForwardingPolicy{
×
UNCOV
1110
                MinHTLCOut:    lnwire.NewMSatFromSatoshis(5),
×
UNCOV
1111
                BaseFee:       lnwire.NewMSatFromSatoshis(1),
×
UNCOV
1112
                TimeLockDelta: defaultDelta,
×
UNCOV
1113
        }
×
UNCOV
1114
        obfuscator := NewMockObfuscator()
×
UNCOV
1115

×
UNCOV
1116
        return &hopNetwork{
×
UNCOV
1117
                feeEstimator: newMockFeeEstimator(),
×
UNCOV
1118
                globalPolicy: globalPolicy,
×
UNCOV
1119
                obfuscator:   obfuscator,
×
UNCOV
1120
                defaultDelta: defaultDelta,
×
UNCOV
1121
        }
×
UNCOV
1122
}
×
1123

1124
func (h *hopNetwork) createChannelLink(server, peer *mockServer,
1125
        channel *lnwallet.LightningChannel,
UNCOV
1126
        decoder *mockIteratorDecoder) (ChannelLink, error) {
×
UNCOV
1127

×
UNCOV
1128
        const (
×
UNCOV
1129
                fwdPkgTimeout       = 15 * time.Second
×
UNCOV
1130
                minFeeUpdateTimeout = 30 * time.Minute
×
UNCOV
1131
                maxFeeUpdateTimeout = 40 * time.Minute
×
UNCOV
1132
        )
×
UNCOV
1133

×
UNCOV
1134
        notifyUpdateChan := make(chan *contractcourt.ContractUpdate)
×
UNCOV
1135
        doneChan := make(chan struct{})
×
UNCOV
1136
        notifyContractUpdate := func(u *contractcourt.ContractUpdate) error {
×
UNCOV
1137
                select {
×
UNCOV
1138
                case notifyUpdateChan <- u:
×
UNCOV
1139
                case <-doneChan:
×
1140
                }
1141

UNCOV
1142
                return nil
×
1143
        }
1144

UNCOV
1145
        getAliases := func(
×
UNCOV
1146
                base lnwire.ShortChannelID) []lnwire.ShortChannelID {
×
UNCOV
1147

×
UNCOV
1148
                return nil
×
UNCOV
1149
        }
×
1150

UNCOV
1151
        forwardPackets := func(linkQuit <-chan struct{}, _ bool,
×
UNCOV
1152
                packets ...*htlcPacket) error {
×
UNCOV
1153

×
UNCOV
1154
                return server.htlcSwitch.ForwardPackets(linkQuit, packets...)
×
UNCOV
1155
        }
×
1156

1157
        link := NewChannelLink(
UNCOV
1158
                ChannelLinkConfig{
×
UNCOV
1159
                        BestHeight:         server.htlcSwitch.BestHeight,
×
UNCOV
1160
                        FwrdingPolicy:      h.globalPolicy,
×
UNCOV
1161
                        Peer:               peer,
×
UNCOV
1162
                        Circuits:           server.htlcSwitch.CircuitModifier(),
×
UNCOV
1163
                        ForwardPackets:     forwardPackets,
×
UNCOV
1164
                        DecodeHopIterators: decoder.DecodeHopIterators,
×
UNCOV
1165
                        ExtractErrorEncrypter: func(*btcec.PublicKey) (
×
UNCOV
1166
                                hop.ErrorEncrypter, lnwire.FailCode) {
×
UNCOV
1167
                                return h.obfuscator, lnwire.CodeNone
×
UNCOV
1168
                        },
×
UNCOV
1169
                        FetchLastChannelUpdate: mockGetChanUpdateMessage,
×
1170
                        Registry:               server.registry,
1171
                        FeeEstimator:           h.feeEstimator,
1172
                        PreimageCache:          server.pCache,
1173
                        UpdateContractSignals: func(*contractcourt.ContractSignals) error {
UNCOV
1174
                                return nil
×
UNCOV
1175
                        },
×
UNCOV
1176
                        NotifyContractUpdate:    notifyContractUpdate,
×
1177
                        ChainEvents:             &contractcourt.ChainEventSubscription{},
1178
                        SyncStates:              true,
1179
                        BatchSize:               10,
1180
                        BatchTicker:             ticker.NewForce(testBatchTimeout),
1181
                        FwdPkgGCTicker:          ticker.NewForce(fwdPkgTimeout),
1182
                        PendingCommitTicker:     ticker.New(2 * time.Minute),
1183
                        MinUpdateTimeout:        minFeeUpdateTimeout,
1184
                        MaxUpdateTimeout:        maxFeeUpdateTimeout,
1185
                        OnChannelFailure:        func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {},
UNCOV
1186
                        OutgoingCltvRejectDelta: 3,
×
1187
                        MaxOutgoingCltvExpiry:   DefaultMaxOutgoingCltvExpiry,
1188
                        MaxFeeAllocation:        DefaultMaxLinkFeeAllocation,
1189
                        MaxAnchorsCommitFeeRate: chainfee.SatPerKVByte(10 * 1000).FeePerKWeight(),
1190
                        NotifyActiveLink:        func(wire.OutPoint) {},
UNCOV
1191
                        NotifyActiveChannel:     func(wire.OutPoint) {},
×
UNCOV
1192
                        NotifyInactiveChannel:   func(wire.OutPoint) {},
×
UNCOV
1193
                        NotifyInactiveLinkEvent: func(wire.OutPoint) {},
×
UNCOV
1194
                        HtlcNotifier:            server.htlcSwitch.cfg.HtlcNotifier,
×
1195
                        GetAliases:              getAliases,
1196
                },
UNCOV
1197
                channel,
×
1198
        )
1199
        if err := server.htlcSwitch.AddLink(link); err != nil {
1200
                return nil, fmt.Errorf("unable to add channel link: %w", err)
UNCOV
1201
        }
×
1202

×
1203
        go func() {
×
1204
                if chanLink, ok := link.(*channelLink); ok {
UNCOV
1205
                        for {
×
UNCOV
1206
                                select {
×
UNCOV
1207
                                case <-notifyUpdateChan:
×
UNCOV
1208
                                case <-chanLink.Quit:
×
UNCOV
1209
                                        close(doneChan)
×
UNCOV
1210
                                        return
×
UNCOV
1211
                                }
×
UNCOV
1212
                        }
×
1213
                }
1214
        }()
1215

1216
        return link, nil
1217
}
UNCOV
1218

×
1219
// twoHopNetwork is used for managing the created cluster of 2 hops.
1220
type twoHopNetwork struct {
1221
        hopNetwork
1222

1223
        aliceServer      *mockServer
1224
        aliceChannelLink *channelLink
1225

1226
        bobServer      *mockServer
1227
        bobChannelLink *channelLink
1228
}
1229

1230
// newTwoHopNetwork function creates and starts the following topology and
1231
// returns the control object to manage this cluster:
1232
//
1233
// alice                      bob
1234
// server - <-connection-> - server
1235
//
1236
//        |                      |
1237
//
1238
// alice htlc               bob htlc
1239
// switch                   switch
1240
//
1241
//        |                      |
1242
//        |                      |
1243
//
1244
// alice                      bob
1245
// channel link           channel link.
1246
func newTwoHopNetwork(t testing.TB,
1247
        aliceChannel, bobChannel *lnwallet.LightningChannel,
1248
        startingHeight uint32) *twoHopNetwork {
1249

UNCOV
1250
        aliceDb := aliceChannel.State().Db.GetParentDB()
×
UNCOV
1251
        bobDb := bobChannel.State().Db.GetParentDB()
×
UNCOV
1252

×
UNCOV
1253
        hopNetwork := newHopNetwork()
×
UNCOV
1254

×
UNCOV
1255
        // Create two peers/servers.
×
UNCOV
1256
        aliceServer, err := newMockServer(
×
UNCOV
1257
                t, "alice", startingHeight, aliceDb, hopNetwork.defaultDelta,
×
UNCOV
1258
        )
×
UNCOV
1259
        require.NoError(t, err, "unable to create alice server")
×
UNCOV
1260
        bobServer, err := newMockServer(
×
UNCOV
1261
                t, "bob", startingHeight, bobDb, hopNetwork.defaultDelta,
×
UNCOV
1262
        )
×
UNCOV
1263
        require.NoError(t, err, "unable to create bob server")
×
UNCOV
1264

×
UNCOV
1265
        // Create mock decoder instead of sphinx one in order to mock the route
×
UNCOV
1266
        // which htlc should follow.
×
UNCOV
1267
        aliceDecoder := newMockIteratorDecoder()
×
UNCOV
1268
        bobDecoder := newMockIteratorDecoder()
×
UNCOV
1269

×
UNCOV
1270
        aliceChannelLink, err := hopNetwork.createChannelLink(
×
UNCOV
1271
                aliceServer, bobServer, aliceChannel, aliceDecoder,
×
UNCOV
1272
        )
×
UNCOV
1273
        if err != nil {
×
UNCOV
1274
                t.Fatal(err)
×
UNCOV
1275
        }
×
1276

×
1277
        bobChannelLink, err := hopNetwork.createChannelLink(
×
1278
                bobServer, aliceServer, bobChannel, bobDecoder,
UNCOV
1279
        )
×
UNCOV
1280
        if err != nil {
×
UNCOV
1281
                t.Fatal(err)
×
UNCOV
1282
        }
×
1283

×
1284
        n := &twoHopNetwork{
×
1285
                aliceServer:      aliceServer,
UNCOV
1286
                aliceChannelLink: aliceChannelLink.(*channelLink),
×
UNCOV
1287

×
UNCOV
1288
                bobServer:      bobServer,
×
UNCOV
1289
                bobChannelLink: bobChannelLink.(*channelLink),
×
UNCOV
1290

×
UNCOV
1291
                hopNetwork: *hopNetwork,
×
UNCOV
1292
        }
×
UNCOV
1293

×
UNCOV
1294
        require.NoError(t, n.start())
×
UNCOV
1295
        t.Cleanup(n.stop)
×
UNCOV
1296

×
UNCOV
1297
        return n
×
UNCOV
1298
}
×
UNCOV
1299

×
1300
// start starts the two hop network alice,bob servers.
1301
func (n *twoHopNetwork) start() error {
1302
        if err := n.aliceServer.Start(); err != nil {
UNCOV
1303
                return err
×
UNCOV
1304
        }
×
1305
        if err := n.bobServer.Start(); err != nil {
×
1306
                n.aliceServer.Stop()
×
UNCOV
1307
                return err
×
1308
        }
×
1309

×
1310
        return waitLinksEligible(map[string]*channelLink{
×
1311
                "alice": n.aliceChannelLink,
UNCOV
1312
                "bob":   n.bobChannelLink,
×
UNCOV
1313
        })
×
UNCOV
1314
}
×
UNCOV
1315

×
1316
// stop stops nodes and cleanup its databases.
1317
func (n *twoHopNetwork) stop() {
1318
        done := make(chan struct{})
UNCOV
1319
        go func() {
×
UNCOV
1320
                n.aliceServer.Stop()
×
UNCOV
1321
                done <- struct{}{}
×
UNCOV
1322
        }()
×
UNCOV
1323

×
UNCOV
1324
        go func() {
×
1325
                n.bobServer.Stop()
UNCOV
1326
                done <- struct{}{}
×
UNCOV
1327
        }()
×
UNCOV
1328

×
UNCOV
1329
        for i := 0; i < 2; i++ {
×
1330
                <-done
UNCOV
1331
        }
×
UNCOV
1332
}
×
UNCOV
1333

×
1334
func (n *twoHopNetwork) makeHoldPayment(sendingPeer, receivingPeer lnpeer.Peer,
1335
        firstHop lnwire.ShortChannelID, hops []*hop.Payload,
1336
        invoiceAmt, htlcAmt lnwire.MilliSatoshi,
1337
        timelock uint32, preimage lntypes.Preimage) chan error {
1338

UNCOV
1339
        paymentErr := make(chan error, 1)
×
UNCOV
1340

×
UNCOV
1341
        sender := sendingPeer.(*mockServer)
×
UNCOV
1342
        receiver := receivingPeer.(*mockServer)
×
UNCOV
1343

×
UNCOV
1344
        // Generate route convert it to blob, and return next destination for
×
UNCOV
1345
        // htlc add request.
×
UNCOV
1346
        blob, err := generateRoute(hops...)
×
UNCOV
1347
        if err != nil {
×
UNCOV
1348
                paymentErr <- err
×
UNCOV
1349
                return paymentErr
×
1350
        }
×
1351

×
1352
        rhash := preimage.Hash()
×
1353

UNCOV
1354
        var payAddr [32]byte
×
UNCOV
1355
        if _, err := crand.Read(payAddr[:]); err != nil {
×
UNCOV
1356
                panic(err)
×
UNCOV
1357
        }
×
1358

×
1359
        // Generate payment: invoice and htlc.
1360
        invoice, htlc, pid, err := generatePaymentWithPreimage(
1361
                invoiceAmt, htlcAmt, timelock, blob,
UNCOV
1362
                nil, rhash, payAddr,
×
UNCOV
1363
        )
×
UNCOV
1364
        if err != nil {
×
UNCOV
1365
                paymentErr <- err
×
UNCOV
1366
                return paymentErr
×
1367
        }
×
1368

×
1369
        // Check who is last in the route and add invoice to server registry.
×
1370
        if err := receiver.registry.AddInvoice(
1371
                context.Background(), *invoice, rhash,
UNCOV
1372
        ); err != nil {
×
UNCOV
1373
                paymentErr <- err
×
UNCOV
1374
                return paymentErr
×
1375
        }
×
1376

×
1377
        // Send payment and expose err channel.
×
1378
        err = sender.htlcSwitch.SendHTLC(firstHop, pid, htlc)
1379
        if err != nil {
UNCOV
1380
                paymentErr <- err
×
UNCOV
1381
                return paymentErr
×
1382
        }
×
1383

×
1384
        go func() {
×
1385
                resultChan, err := sender.htlcSwitch.GetAttemptResult(
UNCOV
1386
                        pid, rhash, newMockDeobfuscator(),
×
UNCOV
1387
                )
×
UNCOV
1388
                if err != nil {
×
UNCOV
1389
                        paymentErr <- err
×
UNCOV
1390
                        return
×
1391
                }
×
1392

×
1393
                result, ok := <-resultChan
×
1394
                if !ok {
UNCOV
1395
                        paymentErr <- fmt.Errorf("shutting down")
×
UNCOV
1396
                        return
×
1397
                }
×
1398

×
1399
                if result.Error != nil {
×
1400
                        paymentErr <- result.Error
UNCOV
1401
                        return
×
UNCOV
1402
                }
×
UNCOV
1403
                paymentErr <- nil
×
UNCOV
1404
        }()
×
UNCOV
1405

×
1406
        return paymentErr
1407
}
UNCOV
1408

×
1409
// waitLinksEligible blocks until all links the provided name-to-link map are
1410
// eligible to forward HTLCs.
1411
func waitLinksEligible(links map[string]*channelLink) error {
1412
        return wait.NoError(func() error {
UNCOV
1413
                for name, link := range links {
×
UNCOV
1414
                        if link.EligibleToForward() {
×
UNCOV
1415
                                continue
×
UNCOV
1416
                        }
×
UNCOV
1417
                        return fmt.Errorf("%s channel link not eligible", name)
×
1418
                }
1419
                return nil
×
1420
        }, 3*time.Second)
UNCOV
1421
}
×
1422

1423
// timeout implements a test level timeout.
1424
func timeout() func() {
1425
        done := make(chan struct{})
UNCOV
1426
        go func() {
×
UNCOV
1427
                select {
×
UNCOV
1428
                case <-time.After(20 * time.Second):
×
UNCOV
1429
                        pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
×
1430

×
1431
                        panic("test timeout")
×
1432
                case <-done:
×
1433
                }
×
UNCOV
1434
        }()
×
1435

1436
        return func() {
1437
                close(done)
UNCOV
1438
        }
×
UNCOV
1439
}
×
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