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

lightningnetwork / lnd / 18530447528

15 Oct 2025 01:25PM UTC coverage: 66.36%. First build
18530447528

Pull #10287

github

web-flow
Merge 069f7c628 into a0044baa8
Pull Request #10287: [Part 2|*] Implement First Part for SQL Backend functions

0 of 880 new or added lines in 4 files covered. (0.0%)

137230 of 206796 relevant lines covered (66.36%)

21221.28 hits per line

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

0.0
/payments/db/sql_converters.go
1
package paymentsdb
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "sort"
7
        "strconv"
8
        "time"
9

10
        "github.com/btcsuite/btcd/btcec/v2"
11
        "github.com/lightningnetwork/lnd/lntypes"
12
        "github.com/lightningnetwork/lnd/lnwire"
13
        "github.com/lightningnetwork/lnd/record"
14
        "github.com/lightningnetwork/lnd/routing/route"
15
        "github.com/lightningnetwork/lnd/sqldb/sqlc"
16
        "github.com/lightningnetwork/lnd/tlv"
17
)
18

19
// dbPaymentToCreationInfo converts database payment data to
20
// PaymentCreationInfo.
21
func dbPaymentToCreationInfo(paymentIdentifier []byte, amountMsat int64,
22
        createdAt time.Time, intentPayload []byte,
NEW
23
        firstHopCustomRecords lnwire.CustomRecords) *PaymentCreationInfo {
×
NEW
24

×
NEW
25
        // This is the payment hash for non-AMP payments and the SetID for AMP
×
NEW
26
        // payments.
×
NEW
27
        var identifier lntypes.Hash
×
NEW
28
        copy(identifier[:], paymentIdentifier)
×
NEW
29

×
NEW
30
        return &PaymentCreationInfo{
×
NEW
31
                PaymentIdentifier:     identifier,
×
NEW
32
                Value:                 lnwire.MilliSatoshi(amountMsat),
×
NEW
33
                CreationTime:          createdAt,
×
NEW
34
                PaymentRequest:        intentPayload,
×
NEW
35
                FirstHopCustomRecords: firstHopCustomRecords,
×
NEW
36
        }
×
NEW
37
}
×
38

39
// dbAttemptToHTLCAttempt converts a database HTLC attempt to an HTLCAttempt.
40
func dbAttemptToHTLCAttempt(
41
        dbAttempt sqlc.FetchHtlcAttemptsForPaymentRow,
42
        hops []sqlc.FetchHopsForAttemptsRow,
43
        hopCustomRecords map[int64][]sqlc.PaymentHopCustomRecord,
44
        routeCustomRecords []sqlc.PaymentAttemptFirstHopCustomRecord) (
NEW
45
        *HTLCAttempt, error) {
×
NEW
46

×
NEW
47
        // Convert route-level first hop custom records to CustomRecords map.
×
NEW
48
        var firstHopWireCustomRecords lnwire.CustomRecords
×
NEW
49
        if len(routeCustomRecords) > 0 {
×
NEW
50
                firstHopWireCustomRecords = make(lnwire.CustomRecords)
×
NEW
51
                for _, record := range routeCustomRecords {
×
NEW
52
                        firstHopWireCustomRecords[uint64(record.Key)] =
×
NEW
53
                                record.Value
×
NEW
54
                }
×
55
        }
56

57
        // Build the route from the database data.
NEW
58
        route, err := dbDataToRoute(
×
NEW
59
                hops, hopCustomRecords, dbAttempt.FirstHopAmountMsat,
×
NEW
60
                dbAttempt.RouteTotalTimeLock, dbAttempt.RouteTotalAmount,
×
NEW
61
                dbAttempt.RouteSourceKey, firstHopWireCustomRecords,
×
NEW
62
        )
×
NEW
63
        if err != nil {
×
NEW
64
                return nil, fmt.Errorf("failed to convert to route: %w",
×
NEW
65
                        err)
×
NEW
66
        }
×
67

NEW
68
        hash, err := lntypes.MakeHash(dbAttempt.PaymentHash)
×
NEW
69
        if err != nil {
×
NEW
70
                return nil, fmt.Errorf("failed to parse payment "+
×
NEW
71
                        "hash: %w", err)
×
NEW
72
        }
×
73

74
        // Create the attempt info.
NEW
75
        var sessionKey [32]byte
×
NEW
76
        copy(sessionKey[:], dbAttempt.SessionKey)
×
NEW
77

×
NEW
78
        info := HTLCAttemptInfo{
×
NEW
79
                AttemptID:   uint64(dbAttempt.AttemptIndex),
×
NEW
80
                sessionKey:  sessionKey,
×
NEW
81
                Route:       *route,
×
NEW
82
                AttemptTime: dbAttempt.AttemptTime,
×
NEW
83
                Hash:        &hash,
×
NEW
84
        }
×
NEW
85

×
NEW
86
        attempt := &HTLCAttempt{
×
NEW
87
                HTLCAttemptInfo: info,
×
NEW
88
        }
×
NEW
89

×
NEW
90
        // Add settlement info if present.
×
NEW
91
        if dbAttempt.ResolutionType.Valid &&
×
NEW
92
                HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
×
NEW
93
                        HTLCAttemptResolutionSettled {
×
NEW
94

×
NEW
95
                var preimage lntypes.Preimage
×
NEW
96
                copy(preimage[:], dbAttempt.SettlePreimage)
×
NEW
97

×
NEW
98
                settleTime := dbAttempt.ResolutionTime.Time
×
NEW
99
                if st, ok := dbAttempt.SettleTime.(time.Time); ok {
×
NEW
100
                        settleTime = st
×
NEW
101
                }
×
102

NEW
103
                attempt.Settle = &HTLCSettleInfo{
×
NEW
104
                        Preimage:   preimage,
×
NEW
105
                        SettleTime: settleTime,
×
NEW
106
                }
×
107
        }
108

109
        // Add failure info if present.
NEW
110
        if dbAttempt.ResolutionType.Valid &&
×
NEW
111
                HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
×
NEW
112
                        HTLCAttemptResolutionFailed {
×
NEW
113

×
NEW
114
                failTime := dbAttempt.ResolutionTime.Time
×
NEW
115
                if ft, ok := dbAttempt.FailTime.(time.Time); ok {
×
NEW
116
                        failTime = ft
×
NEW
117
                }
×
118

NEW
119
                failure := &HTLCFailInfo{
×
NEW
120
                        FailTime: failTime,
×
NEW
121
                }
×
NEW
122

×
NEW
123
                if dbAttempt.HtlcFailReason.Valid {
×
NEW
124
                        failure.Reason = HTLCFailReason(
×
NEW
125
                                dbAttempt.HtlcFailReason.Int32,
×
NEW
126
                        )
×
NEW
127
                }
×
128

NEW
129
                if dbAttempt.FailureSourceIndex.Valid {
×
NEW
130
                        failure.FailureSourceIndex = uint32(
×
NEW
131
                                dbAttempt.FailureSourceIndex.Int32,
×
NEW
132
                        )
×
NEW
133
                }
×
134

135
                // Decode the failure message if present.
NEW
136
                if len(dbAttempt.FailureMsg) > 0 {
×
NEW
137
                        msg, err := lnwire.DecodeFailureMessage(
×
NEW
138
                                bytes.NewReader(dbAttempt.FailureMsg), 0,
×
NEW
139
                        )
×
NEW
140
                        if err != nil {
×
NEW
141
                                return nil, fmt.Errorf("failed to decode "+
×
NEW
142
                                        "failure message: %w", err)
×
NEW
143
                        }
×
NEW
144
                        failure.Message = msg
×
145
                }
146

NEW
147
                attempt.Failure = failure
×
148
        }
149

NEW
150
        return attempt, nil
×
151
}
152

153
// dbDataToRoute converts database route data to a route.Route.
154
func dbDataToRoute(hops []sqlc.FetchHopsForAttemptsRow,
155
        hopCustomRecords map[int64][]sqlc.PaymentHopCustomRecord,
156
        firstHopAmountMsat int64, totalTimeLock int32, totalAmount int64,
157
        sourceKey []byte, firstHopWireCustomRecords lnwire.CustomRecords) (
NEW
158
        *route.Route, error) {
×
NEW
159

×
NEW
160
        if len(hops) == 0 {
×
NEW
161
                return nil, fmt.Errorf("no hops provided")
×
NEW
162
        }
×
163

164
        // Sort hops by hop index.
NEW
165
        sort.Slice(hops, func(i, j int) bool {
×
NEW
166
                return hops[i].HopIndex < hops[j].HopIndex
×
NEW
167
        })
×
168

NEW
169
        routeHops := make([]*route.Hop, len(hops))
×
NEW
170

×
NEW
171
        for i, hop := range hops {
×
NEW
172
                pubKey, err := route.NewVertexFromBytes(hop.PubKey)
×
NEW
173
                if err != nil {
×
NEW
174
                        return nil, fmt.Errorf("failed to parse pub key: %w",
×
NEW
175
                                err)
×
NEW
176
                }
×
177

NEW
178
                var channelID uint64
×
NEW
179
                if hop.Scid != "" {
×
NEW
180
                        // The SCID is stored as a string representation
×
NEW
181
                        // of the uint64.
×
NEW
182
                        var err error
×
NEW
183
                        channelID, err = strconv.ParseUint(hop.Scid, 10, 64)
×
NEW
184
                        if err != nil {
×
NEW
185
                                return nil, fmt.Errorf("failed to parse "+
×
NEW
186
                                        "scid: %w", err)
×
NEW
187
                        }
×
188
                }
189

NEW
190
                routeHop := &route.Hop{
×
NEW
191
                        PubKeyBytes:      pubKey,
×
NEW
192
                        ChannelID:        channelID,
×
NEW
193
                        OutgoingTimeLock: uint32(hop.OutgoingTimeLock),
×
NEW
194
                        AmtToForward:     lnwire.MilliSatoshi(hop.AmtToForward),
×
NEW
195
                }
×
NEW
196

×
NEW
197
                // Add MPP record if present.
×
NEW
198
                if len(hop.MppPaymentAddr) > 0 {
×
NEW
199
                        var paymentAddr [32]byte
×
NEW
200
                        copy(paymentAddr[:], hop.MppPaymentAddr)
×
NEW
201
                        routeHop.MPP = record.NewMPP(
×
NEW
202
                                lnwire.MilliSatoshi(hop.MppTotalMsat.Int64),
×
NEW
203
                                paymentAddr,
×
NEW
204
                        )
×
NEW
205
                }
×
206

207
                // Add AMP record if present.
NEW
208
                if len(hop.AmpRootShare) > 0 {
×
NEW
209
                        var rootShare [32]byte
×
NEW
210
                        copy(rootShare[:], hop.AmpRootShare)
×
NEW
211
                        var setID [32]byte
×
NEW
212
                        copy(setID[:], hop.AmpSetID)
×
NEW
213

×
NEW
214
                        routeHop.AMP = record.NewAMP(
×
NEW
215
                                rootShare, setID,
×
NEW
216
                                uint32(hop.AmpChildIndex.Int32),
×
NEW
217
                        )
×
NEW
218
                }
×
219

220
                // Add blinding point if present (only for introduction node).
NEW
221
                if len(hop.BlindingPoint) > 0 {
×
NEW
222
                        pubKey, err := btcec.ParsePubKey(hop.BlindingPoint)
×
NEW
223
                        if err != nil {
×
NEW
224
                                return nil, fmt.Errorf("failed to parse "+
×
NEW
225
                                        "blinding point: %w", err)
×
NEW
226
                        }
×
NEW
227
                        routeHop.BlindingPoint = pubKey
×
228
                }
229

230
                // Add encrypted data if present (for all blinded hops).
NEW
231
                if len(hop.EncryptedData) > 0 {
×
NEW
232
                        routeHop.EncryptedData = hop.EncryptedData
×
NEW
233
                }
×
234

235
                // Add total amount if present (only for final hop in blinded
236
                // route).
NEW
237
                if hop.BlindedPathTotalAmt.Valid {
×
NEW
238
                        routeHop.TotalAmtMsat = lnwire.MilliSatoshi(
×
NEW
239
                                hop.BlindedPathTotalAmt.Int64,
×
NEW
240
                        )
×
NEW
241
                }
×
242

243
                // Add hop-level custom records.
NEW
244
                if records, ok := hopCustomRecords[hop.ID]; ok {
×
NEW
245
                        routeHop.CustomRecords = make(
×
NEW
246
                                record.CustomSet,
×
NEW
247
                        )
×
NEW
248
                        for _, rec := range records {
×
NEW
249
                                routeHop.CustomRecords[uint64(rec.Key)] =
×
NEW
250
                                        rec.Value
×
NEW
251
                        }
×
252
                }
253

254
                // Add metadata if present.
NEW
255
                if len(hop.MetaData) > 0 {
×
NEW
256
                        routeHop.Metadata = hop.MetaData
×
NEW
257
                }
×
258

NEW
259
                routeHops[i] = routeHop
×
260
        }
261

262
        // Parse the source node public key.
NEW
263
        var sourceNode route.Vertex
×
NEW
264
        copy(sourceNode[:], sourceKey)
×
NEW
265

×
NEW
266
        route := &route.Route{
×
NEW
267
                TotalTimeLock:             uint32(totalTimeLock),
×
NEW
268
                TotalAmount:               lnwire.MilliSatoshi(totalAmount),
×
NEW
269
                SourcePubKey:              sourceNode,
×
NEW
270
                Hops:                      routeHops,
×
NEW
271
                FirstHopWireCustomRecords: firstHopWireCustomRecords,
×
NEW
272
        }
×
NEW
273

×
NEW
274
        // Set the first hop amount if it is set.
×
NEW
275
        if firstHopAmountMsat != 0 {
×
NEW
276
                route.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
×
NEW
277
                        tlv.NewBigSizeT(lnwire.MilliSatoshi(
×
NEW
278
                                firstHopAmountMsat,
×
NEW
279
                        )),
×
NEW
280
                )
×
NEW
281
        }
×
282

NEW
283
        return route, nil
×
284
}
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