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

lightningnetwork / lnd / 13566028875

27 Feb 2025 12:09PM UTC coverage: 49.396% (-9.4%) from 58.748%
13566028875

Pull #9555

github

ellemouton
graph/db: populate the graph cache in Start instead of during construction

In this commit, we move the graph cache population logic out of the
ChannelGraph constructor and into its Start method instead.
Pull Request #9555: graph: extract cache from CRUD [6]

34 of 54 new or added lines in 4 files covered. (62.96%)

27464 existing lines in 436 files now uncovered.

101095 of 204664 relevant lines covered (49.4%)

1.54 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