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

lightningnetwork / lnd / 12231552240

09 Dec 2024 08:17AM UTC coverage: 58.955% (+0.02%) from 58.933%
12231552240

Pull #9242

github

aakselrod
go.mod: update btcwallet to latest to eliminate waddrmgr deadlock
Pull Request #9242: Reapply #8644

24 of 40 new or added lines in 3 files covered. (60.0%)

89 existing lines in 18 files now uncovered.

133525 of 226485 relevant lines covered (58.96%)

19398.62 hits per line

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

87.63
/lnrpc/invoicesrpc/utils.go
1
package invoicesrpc
2

3
import (
4
        "encoding/hex"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/chaincfg"
9
        "github.com/lightningnetwork/lnd/invoices"
10
        "github.com/lightningnetwork/lnd/lnrpc"
11
        "github.com/lightningnetwork/lnd/lnwire"
12
        "github.com/lightningnetwork/lnd/zpay32"
13
)
14

15
// decodePayReq decodes the invoice payment request if present. This is needed,
16
// because not all information is stored in dedicated invoice fields. If there
17
// is no payment request present, a dummy request will be returned. This can
18
// happen with just-in-time inserted keysend invoices.
19
func decodePayReq(invoice *invoices.Invoice,
20
        activeNetParams *chaincfg.Params) (*zpay32.Invoice, error) {
21

22
        paymentRequest := string(invoice.PaymentRequest)
4✔
23
        if paymentRequest == "" {
4✔
24
                preimage := invoice.Terms.PaymentPreimage
4✔
25
                if preimage == nil {
8✔
26
                        return &zpay32.Invoice{}, nil
4✔
27
                }
8✔
28
                hash := [32]byte(preimage.Hash())
4✔
29
                return &zpay32.Invoice{
4✔
30
                        PaymentHash: &hash,
4✔
31
                }, nil
4✔
32
        }
4✔
33

4✔
34
        var err error
35
        decoded, err := zpay32.Decode(paymentRequest, activeNetParams)
36
        if err != nil {
4✔
37
                return nil, fmt.Errorf("unable to decode payment "+
4✔
38
                        "request: %v", err)
4✔
39
        }
×
UNCOV
40
        return decoded, nil
×
UNCOV
41
}
×
42

4✔
43
// CreateRPCInvoice creates an *lnrpc.Invoice from the *invoices.Invoice.
44
func CreateRPCInvoice(invoice *invoices.Invoice,
45
        activeNetParams *chaincfg.Params) (*lnrpc.Invoice, error) {
46

47
        decoded, err := decodePayReq(invoice, activeNetParams)
4✔
48
        if err != nil {
4✔
49
                return nil, err
4✔
50
        }
4✔
UNCOV
51

×
UNCOV
52
        var rHash []byte
×
53
        if decoded.PaymentHash != nil {
54
                rHash = decoded.PaymentHash[:]
4✔
55
        }
8✔
56

4✔
57
        var descHash []byte
4✔
58
        if decoded.DescriptionHash != nil {
59
                descHash = decoded.DescriptionHash[:]
4✔
60
        }
4✔
UNCOV
61

×
UNCOV
62
        fallbackAddr := ""
×
63
        if decoded.FallbackAddr != nil {
64
                fallbackAddr = decoded.FallbackAddr.String()
4✔
65
        }
4✔
UNCOV
66

×
UNCOV
67
        settleDate := int64(0)
×
68
        if !invoice.SettleDate.IsZero() {
69
                settleDate = invoice.SettleDate.Unix()
4✔
70
        }
8✔
71

4✔
72
        // Convert between the `lnrpc` and `routing` types.
4✔
73
        routeHints := CreateRPCRouteHints(decoded.RouteHints)
74

75
        preimage := invoice.Terms.PaymentPreimage
4✔
76
        satAmt := invoice.Terms.Value.ToSatoshis()
4✔
77
        satAmtPaid := invoice.AmtPaid.ToSatoshis()
4✔
78

4✔
79
        isSettled := invoice.State == invoices.ContractSettled
4✔
80

4✔
81
        var state lnrpc.Invoice_InvoiceState
4✔
82
        switch invoice.State {
4✔
83
        case invoices.ContractOpen:
4✔
84
                state = lnrpc.Invoice_OPEN
4✔
85

4✔
86
        case invoices.ContractSettled:
4✔
87
                state = lnrpc.Invoice_SETTLED
88

4✔
89
        case invoices.ContractCanceled:
4✔
90
                state = lnrpc.Invoice_CANCELED
91

4✔
92
        case invoices.ContractAccepted:
4✔
93
                state = lnrpc.Invoice_ACCEPTED
94

4✔
95
        default:
4✔
96
                return nil, fmt.Errorf("unknown invoice state %v",
97
                        invoice.State)
×
UNCOV
98
        }
×
UNCOV
99

×
100
        rpcHtlcs := make([]*lnrpc.InvoiceHTLC, 0, len(invoice.Htlcs))
101
        for key, htlc := range invoice.Htlcs {
102
                var state lnrpc.InvoiceHTLCState
4✔
103
                switch htlc.State {
8✔
104
                case invoices.HtlcStateAccepted:
4✔
105
                        state = lnrpc.InvoiceHTLCState_ACCEPTED
4✔
106
                case invoices.HtlcStateSettled:
4✔
107
                        state = lnrpc.InvoiceHTLCState_SETTLED
4✔
108
                case invoices.HtlcStateCanceled:
4✔
109
                        state = lnrpc.InvoiceHTLCState_CANCELED
4✔
110
                default:
4✔
111
                        return nil, fmt.Errorf("unknown state %v", htlc.State)
4✔
UNCOV
112
                }
×
UNCOV
113

×
114
                rpcHtlc := lnrpc.InvoiceHTLC{
115
                        ChanId:          key.ChanID.ToUint64(),
116
                        HtlcIndex:       key.HtlcID,
4✔
117
                        AcceptHeight:    int32(htlc.AcceptHeight),
4✔
118
                        AcceptTime:      htlc.AcceptTime.Unix(),
4✔
119
                        ExpiryHeight:    int32(htlc.Expiry),
4✔
120
                        AmtMsat:         uint64(htlc.Amt),
4✔
121
                        State:           state,
4✔
122
                        CustomRecords:   htlc.CustomRecords,
4✔
123
                        MppTotalAmtMsat: uint64(htlc.MppTotalAmt),
4✔
124
                }
4✔
125

4✔
126
                // The custom channel data is currently just the raw bytes of
4✔
127
                // the encoded custom records.
4✔
128
                customData, err := lnwire.CustomRecords(
4✔
129
                        htlc.CustomRecords,
4✔
130
                ).Serialize()
4✔
131
                if err != nil {
4✔
132
                        return nil, err
4✔
133
                }
4✔
UNCOV
134
                rpcHtlc.CustomChannelData = customData
×
UNCOV
135

×
136
                // Populate any fields relevant to AMP payments.
4✔
137
                if htlc.AMP != nil {
4✔
138
                        rootShare := htlc.AMP.Record.RootShare()
4✔
139
                        setID := htlc.AMP.Record.SetID()
8✔
140

4✔
141
                        var preimage []byte
4✔
142
                        if htlc.AMP.Preimage != nil {
4✔
143
                                preimage = htlc.AMP.Preimage[:]
4✔
144
                        }
8✔
145

4✔
146
                        rpcHtlc.Amp = &lnrpc.AMP{
4✔
147
                                RootShare:  rootShare[:],
148
                                SetId:      setID[:],
4✔
149
                                ChildIndex: htlc.AMP.Record.ChildIndex(),
4✔
150
                                Hash:       htlc.AMP.Hash[:],
4✔
151
                                Preimage:   preimage,
4✔
152
                        }
4✔
153
                }
4✔
154

4✔
155
                // Only report resolved times if htlc is resolved.
156
                if htlc.State != invoices.HtlcStateAccepted {
157
                        rpcHtlc.ResolveTime = htlc.ResolveTime.Unix()
158
                }
8✔
159

4✔
160
                rpcHtlcs = append(rpcHtlcs, &rpcHtlc)
4✔
161
        }
162

4✔
163
        rpcInvoice := &lnrpc.Invoice{
164
                Memo:            string(invoice.Memo),
165
                RHash:           rHash,
166
                Value:           int64(satAmt),
8✔
167
                ValueMsat:       int64(invoice.Terms.Value),
4✔
168
                CreationDate:    invoice.CreationDate.Unix(),
4✔
169
                SettleDate:      settleDate,
170
                Settled:         isSettled,
4✔
171
                PaymentRequest:  string(invoice.PaymentRequest),
4✔
172
                DescriptionHash: descHash,
4✔
173
                Expiry:          int64(invoice.Terms.Expiry.Seconds()),
4✔
174
                CltvExpiry:      uint64(invoice.Terms.FinalCltvDelta),
4✔
175
                FallbackAddr:    fallbackAddr,
4✔
176
                RouteHints:      routeHints,
4✔
177
                AddIndex:        invoice.AddIndex,
4✔
178
                Private:         len(routeHints) > 0,
4✔
179
                SettleIndex:     invoice.SettleIndex,
4✔
180
                AmtPaidSat:      int64(satAmtPaid),
4✔
181
                AmtPaidMsat:     int64(invoice.AmtPaid),
4✔
182
                AmtPaid:         int64(invoice.AmtPaid),
4✔
183
                State:           state,
4✔
184
                Htlcs:           rpcHtlcs,
4✔
185
                Features:        CreateRPCFeatures(invoice.Terms.Features),
4✔
186
                IsKeysend:       invoice.IsKeysend(),
4✔
187
                PaymentAddr:     invoice.Terms.PaymentAddr[:],
4✔
188
                IsAmp:           invoice.IsAMP(),
4✔
189
                IsBlinded:       invoice.IsBlinded(),
4✔
190
        }
4✔
191

4✔
192
        rpcInvoice.AmpInvoiceState = make(map[string]*lnrpc.AMPInvoiceState)
4✔
193
        for setID, ampState := range invoice.AMPState {
4✔
194
                setIDStr := hex.EncodeToString(setID[:])
4✔
195

4✔
196
                var state lnrpc.InvoiceHTLCState
4✔
197
                switch ampState.State {
4✔
198
                case invoices.HtlcStateAccepted:
4✔
199
                        state = lnrpc.InvoiceHTLCState_ACCEPTED
4✔
200
                case invoices.HtlcStateSettled:
8✔
201
                        state = lnrpc.InvoiceHTLCState_SETTLED
4✔
202
                case invoices.HtlcStateCanceled:
4✔
203
                        state = lnrpc.InvoiceHTLCState_CANCELED
4✔
204
                default:
4✔
205
                        return nil, fmt.Errorf("unknown state %v", ampState.State)
×
UNCOV
206
                }
×
207

4✔
208
                rpcInvoice.AmpInvoiceState[setIDStr] = &lnrpc.AMPInvoiceState{
4✔
UNCOV
209
                        State:       state,
×
UNCOV
210
                        SettleIndex: ampState.SettleIndex,
×
UNCOV
211
                        SettleTime:  ampState.SettleDate.Unix(),
×
UNCOV
212
                        AmtPaidMsat: int64(ampState.AmtPaid),
×
213
                }
214

215
                // If at least one of the present HTLC sets show up as being
4✔
216
                // settled, then we'll mark the invoice itself as being
4✔
217
                // settled.
4✔
218
                if ampState.State == invoices.HtlcStateSettled {
4✔
219
                        rpcInvoice.Settled = true // nolint:staticcheck
4✔
220
                        rpcInvoice.State = lnrpc.Invoice_SETTLED
4✔
221
                }
4✔
222
        }
4✔
223

4✔
224
        if preimage != nil {
4✔
225
                rpcInvoice.RPreimage = preimage[:]
8✔
226
        }
4✔
227

4✔
228
        return rpcInvoice, nil
4✔
229
}
230

231
// CreateRPCFeatures maps a feature vector into a list of lnrpc.Features.
8✔
232
func CreateRPCFeatures(fv *lnwire.FeatureVector) map[uint32]*lnrpc.Feature {
4✔
233
        if fv == nil {
4✔
234
                return nil
235
        }
4✔
236

237
        features := fv.Features()
238
        rpcFeatures := make(map[uint32]*lnrpc.Feature, len(features))
239
        for bit := range features {
4✔
240
                rpcFeatures[uint32(bit)] = &lnrpc.Feature{
4✔
UNCOV
241
                        Name:       fv.Name(bit),
×
UNCOV
242
                        IsRequired: bit.IsRequired(),
×
243
                        IsKnown:    fv.IsKnown(bit),
244
                }
4✔
245
        }
4✔
246

8✔
247
        return rpcFeatures
4✔
248
}
4✔
249

4✔
250
// CreateRPCRouteHints takes in the decoded form of an invoice's route hints
4✔
251
// and converts them into the lnrpc type.
4✔
252
func CreateRPCRouteHints(routeHints [][]zpay32.HopHint) []*lnrpc.RouteHint {
4✔
253
        var res []*lnrpc.RouteHint
254

4✔
255
        for _, route := range routeHints {
256
                hopHints := make([]*lnrpc.HopHint, 0, len(route))
257
                for _, hop := range route {
258
                        pubKey := hex.EncodeToString(
259
                                hop.NodeID.SerializeCompressed(),
4✔
260
                        )
4✔
261

4✔
262
                        hint := &lnrpc.HopHint{
8✔
263
                                NodeId:                    pubKey,
4✔
264
                                ChanId:                    hop.ChannelID,
8✔
265
                                FeeBaseMsat:               hop.FeeBaseMSat,
4✔
266
                                FeeProportionalMillionths: hop.FeeProportionalMillionths,
4✔
267
                                CltvExpiryDelta:           uint32(hop.CLTVExpiryDelta),
4✔
268
                        }
4✔
269

4✔
270
                        hopHints = append(hopHints, hint)
4✔
271
                }
4✔
272

4✔
273
                routeHint := &lnrpc.RouteHint{HopHints: hopHints}
4✔
274
                res = append(res, routeHint)
4✔
275
        }
4✔
276

4✔
277
        return res
4✔
278
}
4✔
279

280
// CreateRPCBlindedPayments takes a set of zpay32.BlindedPaymentPath and
4✔
281
// converts them into a set of lnrpc.BlindedPaymentPaths.
4✔
282
func CreateRPCBlindedPayments(blindedPaths []*zpay32.BlindedPaymentPath) (
283
        []*lnrpc.BlindedPaymentPath, error) {
284

4✔
285
        var res []*lnrpc.BlindedPaymentPath
286
        for _, path := range blindedPaths {
287
                features := path.Features.Features()
288
                var featuresSlice []lnrpc.FeatureBit
289
                for feature := range features {
290
                        featuresSlice = append(
4✔
291
                                featuresSlice, lnrpc.FeatureBit(feature),
4✔
292
                        )
4✔
293
                }
8✔
294

4✔
295
                if len(path.Hops) == 0 {
4✔
296
                        return nil, fmt.Errorf("each blinded path must " +
4✔
297
                                "contain at least one hop")
×
298
                }
×
UNCOV
299

×
UNCOV
300
                var hops []*lnrpc.BlindedHop
×
301
                for _, hop := range path.Hops {
302
                        blindedNodeID := hop.BlindedNodePub.
4✔
UNCOV
303
                                SerializeCompressed()
×
UNCOV
304
                        hops = append(hops, &lnrpc.BlindedHop{
×
UNCOV
305
                                BlindedNode:   blindedNodeID,
×
306
                                EncryptedData: hop.CipherText,
307
                        })
4✔
308
                }
8✔
309

4✔
310
                introNode := path.Hops[0].BlindedNodePub
4✔
311
                firstBlindingPoint := path.FirstEphemeralBlindingPoint
4✔
312

4✔
313
                blindedPath := &lnrpc.BlindedPath{
4✔
314
                        IntroductionNode: introNode.SerializeCompressed(),
4✔
315
                        BlindingPoint: firstBlindingPoint.
4✔
316
                                SerializeCompressed(),
317
                        BlindedHops: hops,
4✔
318
                }
4✔
319

4✔
320
                res = append(res, &lnrpc.BlindedPaymentPath{
4✔
321
                        BlindedPath:         blindedPath,
4✔
322
                        BaseFeeMsat:         uint64(path.FeeBaseMsat),
4✔
323
                        ProportionalFeeRate: path.FeeRate,
4✔
324
                        TotalCltvDelta:      uint32(path.CltvExpiryDelta),
4✔
325
                        HtlcMinMsat:         path.HTLCMinMsat,
4✔
326
                        HtlcMaxMsat:         path.HTLCMaxMsat,
4✔
327
                        Features:            featuresSlice,
4✔
328
                })
4✔
329
        }
4✔
330

4✔
331
        return res, nil
4✔
332
}
4✔
333

4✔
334
// CreateZpay32HopHints takes in the lnrpc form of route hints and converts them
4✔
335
// into an invoice decoded form.
4✔
336
func CreateZpay32HopHints(routeHints []*lnrpc.RouteHint) ([][]zpay32.HopHint, error) {
337
        var res [][]zpay32.HopHint
338
        for _, route := range routeHints {
4✔
339
                hopHints := make([]zpay32.HopHint, 0, len(route.HopHints))
340
                for _, hop := range route.HopHints {
341
                        pubKeyBytes, err := hex.DecodeString(hop.NodeId)
342
                        if err != nil {
343
                                return nil, err
4✔
344
                        }
4✔
345
                        p, err := btcec.ParsePubKey(pubKeyBytes)
8✔
346
                        if err != nil {
4✔
347
                                return nil, err
8✔
348
                        }
4✔
349
                        hopHints = append(hopHints, zpay32.HopHint{
4✔
UNCOV
350
                                NodeID:                    p,
×
UNCOV
351
                                ChannelID:                 hop.ChanId,
×
352
                                FeeBaseMSat:               hop.FeeBaseMsat,
4✔
353
                                FeeProportionalMillionths: hop.FeeProportionalMillionths,
4✔
UNCOV
354
                                CLTVExpiryDelta:           uint16(hop.CltvExpiryDelta),
×
UNCOV
355
                        })
×
356
                }
4✔
357
                res = append(res, hopHints)
4✔
358
        }
4✔
359
        return res, nil
4✔
360
}
4✔
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