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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

0.0
/invoices/test_utils.go
1
package invoices
2

3
import (
4
        "crypto/rand"
5
        "encoding/binary"
6
        "encoding/hex"
7
        "sync"
8
        "testing"
9
        "time"
10

11
        "github.com/btcsuite/btcd/btcec/v2"
12
        "github.com/btcsuite/btcd/btcec/v2/ecdsa"
13
        "github.com/btcsuite/btcd/chaincfg"
14
        "github.com/btcsuite/btcd/chaincfg/chainhash"
15
        "github.com/lightningnetwork/lnd/chainntnfs"
16
        "github.com/lightningnetwork/lnd/clock"
17
        "github.com/lightningnetwork/lnd/lntypes"
18
        "github.com/lightningnetwork/lnd/lnwire"
19
        "github.com/lightningnetwork/lnd/zpay32"
20
        "github.com/stretchr/testify/require"
21
)
22

23
type mockChainNotifier struct {
24
        chainntnfs.ChainNotifier
25

26
        blockChan chan *chainntnfs.BlockEpoch
27
}
28

UNCOV
29
func newMockNotifier() *mockChainNotifier {
×
UNCOV
30
        return &mockChainNotifier{
×
UNCOV
31
                blockChan: make(chan *chainntnfs.BlockEpoch),
×
UNCOV
32
        }
×
UNCOV
33
}
×
34

35
// RegisterBlockEpochNtfn mocks a block epoch notification, using the mock's
36
// block channel to deliver blocks to the client.
37
func (m *mockChainNotifier) RegisterBlockEpochNtfn(*chainntnfs.BlockEpoch) (
UNCOV
38
        *chainntnfs.BlockEpochEvent, error) {
×
UNCOV
39

×
UNCOV
40
        return &chainntnfs.BlockEpochEvent{
×
UNCOV
41
                Epochs: m.blockChan,
×
UNCOV
42
                Cancel: func() {},
×
43
        }, nil
44
}
45

46
const (
47
        testCurrentHeight = int32(1)
48
)
49

50
var (
51
        testTimeout = 5 * time.Second
52

53
        testTime = time.Date(2018, time.February, 2, 14, 0, 0, 0, time.UTC)
54

55
        testPrivKeyBytes, _ = hex.DecodeString(
56
                "e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2d" +
57
                        "b734",
58
        )
59

60
        testPrivKey, _ = btcec.PrivKeyFromBytes(testPrivKeyBytes)
61

62
        testInvoiceDescription = "coffee"
63

64
        testInvoiceAmount = lnwire.MilliSatoshi(100000)
65

66
        testNetParams = &chaincfg.MainNetParams
67

68
        testMessageSigner = zpay32.MessageSigner{
UNCOV
69
                SignCompact: func(msg []byte) ([]byte, error) {
×
UNCOV
70
                        hash := chainhash.HashB(msg)
×
UNCOV
71
                        sig := ecdsa.SignCompact(testPrivKey, hash, true)
×
UNCOV
72

×
UNCOV
73
                        return sig, nil
×
UNCOV
74
                },
×
75
        }
76

77
        testFeatures = lnwire.NewFeatureVector(
78
                nil, lnwire.Features,
79
        )
80
)
81

82
func newTestInvoice(t *testing.T, preimage lntypes.Preimage,
UNCOV
83
        timestamp time.Time, expiry time.Duration) *Invoice {
×
UNCOV
84

×
UNCOV
85
        t.Helper()
×
UNCOV
86

×
UNCOV
87
        if expiry == 0 {
×
88
                expiry = time.Hour
×
89
        }
×
90

UNCOV
91
        var payAddr [32]byte
×
UNCOV
92
        if _, err := rand.Read(payAddr[:]); err != nil {
×
93
                t.Fatalf("unable to generate payment addr: %v", err)
×
94
        }
×
95

UNCOV
96
        rawInvoice, err := zpay32.NewInvoice(
×
UNCOV
97
                testNetParams,
×
UNCOV
98
                preimage.Hash(),
×
UNCOV
99
                timestamp,
×
UNCOV
100
                zpay32.Amount(testInvoiceAmount),
×
UNCOV
101
                zpay32.Description(testInvoiceDescription),
×
UNCOV
102
                zpay32.Expiry(expiry),
×
UNCOV
103
                zpay32.PaymentAddr(payAddr),
×
UNCOV
104
        )
×
UNCOV
105
        require.NoError(t, err, "Error while creating new invoice")
×
UNCOV
106

×
UNCOV
107
        paymentRequest, err := rawInvoice.Encode(testMessageSigner)
×
UNCOV
108

×
UNCOV
109
        require.NoError(t, err, "Error while encoding payment request")
×
UNCOV
110

×
UNCOV
111
        return &Invoice{
×
UNCOV
112
                Terms: ContractTerm{
×
UNCOV
113
                        PaymentPreimage: &preimage,
×
UNCOV
114
                        PaymentAddr:     payAddr,
×
UNCOV
115
                        Value:           testInvoiceAmount,
×
UNCOV
116
                        Expiry:          expiry,
×
UNCOV
117
                        Features:        testFeatures,
×
UNCOV
118
                },
×
UNCOV
119
                PaymentRequest: []byte(paymentRequest),
×
UNCOV
120
                CreationDate:   timestamp,
×
UNCOV
121
        }
×
122
}
123

124
// invoiceExpiryTestData simply holds generated expired and pending invoices.
125
type invoiceExpiryTestData struct {
126
        expiredInvoices map[lntypes.Hash]*Invoice
127
        pendingInvoices map[lntypes.Hash]*Invoice
128
}
129

130
// generateInvoiceExpiryTestData generates the specified number of fake expired
131
// and pending invoices anchored to the passed now timestamp.
132
func generateInvoiceExpiryTestData(
133
        t *testing.T, now time.Time,
UNCOV
134
        offset, numExpired, numPending int) invoiceExpiryTestData {
×
UNCOV
135

×
UNCOV
136
        t.Helper()
×
UNCOV
137

×
UNCOV
138
        var testData invoiceExpiryTestData
×
UNCOV
139

×
UNCOV
140
        testData.expiredInvoices = make(map[lntypes.Hash]*Invoice)
×
UNCOV
141
        testData.pendingInvoices = make(map[lntypes.Hash]*Invoice)
×
UNCOV
142

×
UNCOV
143
        expiredCreationDate := now.Add(-24 * time.Hour)
×
UNCOV
144

×
UNCOV
145
        for i := 1; i <= numExpired; i++ {
×
UNCOV
146
                var preimage lntypes.Preimage
×
UNCOV
147
                binary.BigEndian.PutUint32(preimage[:4], uint32(offset+i))
×
UNCOV
148
                duration := (i + offset) % 24
×
UNCOV
149
                expiry := time.Duration(duration) * time.Hour
×
UNCOV
150
                invoice := newTestInvoice(
×
UNCOV
151
                        t, preimage, expiredCreationDate, expiry,
×
UNCOV
152
                )
×
UNCOV
153
                testData.expiredInvoices[preimage.Hash()] = invoice
×
UNCOV
154
        }
×
155

UNCOV
156
        for i := 1; i <= numPending; i++ {
×
UNCOV
157
                var preimage lntypes.Preimage
×
UNCOV
158
                binary.BigEndian.PutUint32(preimage[4:], uint32(offset+i))
×
UNCOV
159
                duration := (i + offset) % 24
×
UNCOV
160
                expiry := time.Duration(duration) * time.Hour
×
UNCOV
161
                invoice := newTestInvoice(t, preimage, now, expiry)
×
UNCOV
162
                testData.pendingInvoices[preimage.Hash()] = invoice
×
UNCOV
163
        }
×
164

UNCOV
165
        return testData
×
166
}
167

168
type hodlExpiryTest struct {
169
        hash         lntypes.Hash
170
        state        ContractState
171
        stateLock    sync.Mutex
172
        mockNotifier *mockChainNotifier
173
        mockClock    *clock.TestClock
174
        cancelChan   chan lntypes.Hash
175
        watcher      *InvoiceExpiryWatcher
176
}
177

UNCOV
178
func (h *hodlExpiryTest) setState(state ContractState) {
×
UNCOV
179
        h.stateLock.Lock()
×
UNCOV
180
        defer h.stateLock.Unlock()
×
UNCOV
181

×
UNCOV
182
        h.state = state
×
UNCOV
183
}
×
184

UNCOV
185
func (h *hodlExpiryTest) announceBlock(t *testing.T, height uint32) {
×
UNCOV
186
        t.Helper()
×
UNCOV
187

×
UNCOV
188
        select {
×
189
        case h.mockNotifier.blockChan <- &chainntnfs.BlockEpoch{
190
                Height: int32(height),
UNCOV
191
        }:
×
192

193
        case <-time.After(testTimeout):
×
194
                t.Fatalf("block %v not consumed", height)
×
195
        }
196
}
197

UNCOV
198
func (h *hodlExpiryTest) assertCanceled(t *testing.T, expected lntypes.Hash) {
×
UNCOV
199
        t.Helper()
×
UNCOV
200

×
UNCOV
201
        select {
×
UNCOV
202
        case actual := <-h.cancelChan:
×
UNCOV
203
                require.Equal(t, expected, actual)
×
204

205
        case <-time.After(testTimeout):
×
206
                t.Fatalf("invoice: %v not canceled", h.hash)
×
207
        }
208
}
209

210
// setupHodlExpiry creates a hodl invoice in our expiry watcher and runs an
211
// arbitrary update function which advances the invoices's state.
212
func setupHodlExpiry(t *testing.T, creationDate time.Time,
213
        expiry time.Duration, heightDelta uint32,
214
        startState ContractState,
UNCOV
215
        startHtlcs []*InvoiceHTLC) *hodlExpiryTest {
×
UNCOV
216

×
UNCOV
217
        t.Helper()
×
UNCOV
218

×
UNCOV
219
        mockNotifier := newMockNotifier()
×
UNCOV
220
        mockClock := clock.NewTestClock(testTime)
×
UNCOV
221

×
UNCOV
222
        test := &hodlExpiryTest{
×
UNCOV
223
                state: startState,
×
UNCOV
224
                watcher: NewInvoiceExpiryWatcher(
×
UNCOV
225
                        mockClock, heightDelta, uint32(testCurrentHeight), nil,
×
UNCOV
226
                        mockNotifier,
×
UNCOV
227
                ),
×
UNCOV
228
                cancelChan:   make(chan lntypes.Hash),
×
UNCOV
229
                mockNotifier: mockNotifier,
×
UNCOV
230
                mockClock:    mockClock,
×
UNCOV
231
        }
×
UNCOV
232

×
UNCOV
233
        // Use an unbuffered channel to block on cancel calls so that the test
×
UNCOV
234
        // does not exit before we've processed all the invoices we expect.
×
UNCOV
235
        cancelImpl := func(paymentHash lntypes.Hash, force bool) error {
×
UNCOV
236
                test.stateLock.Lock()
×
UNCOV
237
                currentState := test.state
×
UNCOV
238
                test.stateLock.Unlock()
×
UNCOV
239

×
UNCOV
240
                if currentState != ContractOpen && !force {
×
241
                        return nil
×
242
                }
×
243

UNCOV
244
                select {
×
UNCOV
245
                case test.cancelChan <- paymentHash:
×
246
                case <-time.After(testTimeout):
×
247
                }
248

UNCOV
249
                return nil
×
250
        }
251

UNCOV
252
        require.NoError(t, test.watcher.Start(cancelImpl))
×
UNCOV
253

×
UNCOV
254
        // We set preimage and hash so that we can use our existing test
×
UNCOV
255
        // helpers. In practice we would only have the hash, but this does not
×
UNCOV
256
        // affect what we're testing at all.
×
UNCOV
257
        preimage := lntypes.Preimage{1}
×
UNCOV
258
        test.hash = preimage.Hash()
×
UNCOV
259

×
UNCOV
260
        invoice := newTestInvoice(t, preimage, creationDate, expiry)
×
UNCOV
261
        invoice.State = startState
×
UNCOV
262
        invoice.HodlInvoice = true
×
UNCOV
263
        invoice.Htlcs = make(map[CircuitKey]*InvoiceHTLC)
×
UNCOV
264

×
UNCOV
265
        // If we have any htlcs, add them with unique circult keys.
×
UNCOV
266
        for i, htlc := range startHtlcs {
×
UNCOV
267
                key := CircuitKey{
×
UNCOV
268
                        HtlcID: uint64(i),
×
UNCOV
269
                }
×
UNCOV
270

×
UNCOV
271
                invoice.Htlcs[key] = htlc
×
UNCOV
272
        }
×
273

274
        // Create an expiry entry for our invoice in its starting state. This
275
        // mimics adding invoices to the watcher on start.
UNCOV
276
        entry := makeInvoiceExpiry(test.hash, invoice)
×
UNCOV
277
        test.watcher.AddInvoices(entry)
×
UNCOV
278

×
UNCOV
279
        return test
×
280
}
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