Skip to content

Commit 63aa866

Browse files
committed
paymentsdb: implement QueryPayments for sql backend
1 parent 32ce143 commit 63aa866

File tree

2 files changed

+669
-3
lines changed

2 files changed

+669
-3
lines changed

payments/db/sql_converters.go

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
package paymentsdb
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"sort"
7+
"time"
8+
9+
"github.com/btcsuite/btcd/btcec/v2"
10+
"github.com/lightningnetwork/lnd/lntypes"
11+
"github.com/lightningnetwork/lnd/lnwire"
12+
"github.com/lightningnetwork/lnd/record"
13+
"github.com/lightningnetwork/lnd/routing/route"
14+
"github.com/lightningnetwork/lnd/sqldb/sqlc"
15+
"github.com/lightningnetwork/lnd/tlv"
16+
)
17+
18+
// dbPaymentToCreationInfo converts database payment data to
19+
// PaymentCreationInfo.
20+
func dbPaymentToCreationInfo(paymentIdentifier []byte, amountMsat int64,
21+
createdAt time.Time, intentPayload []byte,
22+
firstHopCustomRecords lnwire.CustomRecords) *PaymentCreationInfo {
23+
24+
// This is the payment hash for non-AMP payments and the SetID for AMP
25+
// payments.
26+
var identifier lntypes.Hash
27+
copy(identifier[:], paymentIdentifier)
28+
29+
return &PaymentCreationInfo{
30+
PaymentIdentifier: identifier,
31+
Value: lnwire.MilliSatoshi(amountMsat),
32+
CreationTime: createdAt,
33+
PaymentRequest: intentPayload,
34+
FirstHopCustomRecords: firstHopCustomRecords,
35+
}
36+
}
37+
38+
// dbAttemptToHTLCAttempt converts a database HTLC attempt to an HTLCAttempt.
39+
func dbAttemptToHTLCAttempt(
40+
dbAttempt sqlc.FetchHtlcAttemptsForPaymentRow,
41+
hops []sqlc.FetchHopsForAttemptsRow,
42+
hopCustomRecords map[int64][]sqlc.PaymentHopCustomRecord,
43+
routeCustomRecords []sqlc.PaymentAttemptFirstHopCustomRecord) (
44+
*HTLCAttempt, error) {
45+
46+
// Convert route-level first hop custom records to CustomRecords map.
47+
var firstHopWireCustomRecords lnwire.CustomRecords
48+
if len(routeCustomRecords) > 0 {
49+
firstHopWireCustomRecords = make(lnwire.CustomRecords)
50+
for _, record := range routeCustomRecords {
51+
firstHopWireCustomRecords[uint64(record.Key)] =
52+
record.Value
53+
}
54+
}
55+
56+
// Build the route from the database data.
57+
route, err := dbDataToRoute(
58+
hops, hopCustomRecords, dbAttempt.FirstHopAmountMsat,
59+
dbAttempt.RouteTotalTimeLock, dbAttempt.RouteTotalAmount,
60+
dbAttempt.RouteSourceKey, firstHopWireCustomRecords,
61+
)
62+
if err != nil {
63+
return nil, fmt.Errorf("failed to convert to route: %w",
64+
err)
65+
}
66+
67+
// Parse the hash if available.
68+
var hash *lntypes.Hash
69+
if len(dbAttempt.PaymentHash) > 0 {
70+
h, err := lntypes.MakeHash(dbAttempt.PaymentHash)
71+
if err != nil {
72+
return nil, fmt.Errorf("failed to parse payment "+
73+
"hash: %w", err)
74+
}
75+
hash = &h
76+
}
77+
78+
// Create the attempt info.
79+
var sessionKey [32]byte
80+
copy(sessionKey[:], dbAttempt.SessionKey)
81+
82+
info := HTLCAttemptInfo{
83+
AttemptID: uint64(dbAttempt.AttemptIndex),
84+
sessionKey: sessionKey,
85+
Route: *route,
86+
AttemptTime: dbAttempt.AttemptTime,
87+
Hash: hash,
88+
}
89+
90+
attempt := &HTLCAttempt{
91+
HTLCAttemptInfo: info,
92+
}
93+
94+
// Add settlement info if present.
95+
if dbAttempt.ResolutionType.Valid &&
96+
HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
97+
HTLCAttemptResolutionSettled {
98+
99+
var preimage lntypes.Preimage
100+
copy(preimage[:], dbAttempt.SettlePreimage)
101+
102+
settleTime := dbAttempt.ResolutionTime.Time
103+
if st, ok := dbAttempt.SettleTime.(time.Time); ok {
104+
settleTime = st
105+
}
106+
107+
attempt.Settle = &HTLCSettleInfo{
108+
Preimage: preimage,
109+
SettleTime: settleTime,
110+
}
111+
}
112+
113+
// Add failure info if present.
114+
if dbAttempt.ResolutionType.Valid &&
115+
HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
116+
HTLCAttemptResolutionFailed {
117+
118+
failTime := dbAttempt.ResolutionTime.Time
119+
if ft, ok := dbAttempt.FailTime.(time.Time); ok {
120+
failTime = ft
121+
}
122+
123+
failure := &HTLCFailInfo{
124+
FailTime: failTime,
125+
}
126+
127+
if dbAttempt.HtlcFailReason.Valid {
128+
failure.Reason = HTLCFailReason(
129+
dbAttempt.HtlcFailReason.Int32,
130+
)
131+
}
132+
133+
if dbAttempt.FailureSourceIndex.Valid {
134+
failure.FailureSourceIndex = uint32(
135+
dbAttempt.FailureSourceIndex.Int32,
136+
)
137+
}
138+
139+
// Decode the failure message if present.
140+
if len(dbAttempt.FailureMsg) > 0 {
141+
msg, err := lnwire.DecodeFailureMessage(
142+
bytes.NewReader(dbAttempt.FailureMsg), 0,
143+
)
144+
if err != nil {
145+
return nil, fmt.Errorf("failed to decode "+
146+
"failure message: %w", err)
147+
}
148+
failure.Message = msg
149+
}
150+
151+
attempt.Failure = failure
152+
}
153+
154+
return attempt, nil
155+
}
156+
157+
// dbDataToRoute converts database route data to a route.Route.
158+
func dbDataToRoute(hops []sqlc.FetchHopsForAttemptsRow,
159+
hopCustomRecords map[int64][]sqlc.PaymentHopCustomRecord,
160+
firstHopAmountMsat int64, totalTimeLock int32, totalAmount int64,
161+
sourceKey []byte, firstHopWireCustomRecords lnwire.CustomRecords) (
162+
*route.Route, error) {
163+
164+
if len(hops) == 0 {
165+
return nil, fmt.Errorf("no hops provided")
166+
}
167+
168+
// Sort hops by hop index.
169+
sort.Slice(hops, func(i, j int) bool {
170+
return hops[i].HopIndex < hops[j].HopIndex
171+
})
172+
173+
routeHops := make([]*route.Hop, len(hops))
174+
175+
for i, hop := range hops {
176+
pubKey, err := route.NewVertexFromBytes(hop.PubKey)
177+
if err != nil {
178+
return nil, fmt.Errorf("failed to parse pub key: %w",
179+
err)
180+
}
181+
182+
var channelID uint64
183+
if hop.Scid != "" {
184+
// The SCID is stored as a string representation
185+
// of the uint64.
186+
_, err := fmt.Sscanf(hop.Scid, "%d", &channelID)
187+
if err != nil {
188+
return nil, fmt.Errorf("failed to parse "+
189+
"scid: %w", err)
190+
}
191+
}
192+
193+
routeHop := &route.Hop{
194+
PubKeyBytes: pubKey,
195+
ChannelID: channelID,
196+
OutgoingTimeLock: uint32(hop.OutgoingTimeLock),
197+
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForward),
198+
}
199+
200+
// Add MPP record if present.
201+
if len(hop.MppPaymentAddr) > 0 {
202+
var paymentAddr [32]byte
203+
copy(paymentAddr[:], hop.MppPaymentAddr)
204+
routeHop.MPP = record.NewMPP(
205+
lnwire.MilliSatoshi(hop.MppTotalMsat.Int64),
206+
paymentAddr,
207+
)
208+
}
209+
210+
// Add AMP record if present.
211+
if len(hop.AmpRootShare) > 0 {
212+
var rootShare [32]byte
213+
copy(rootShare[:], hop.AmpRootShare)
214+
var setID [32]byte
215+
copy(setID[:], hop.AmpSetID)
216+
217+
routeHop.AMP = record.NewAMP(
218+
rootShare, setID,
219+
uint32(hop.AmpChildIndex.Int32),
220+
)
221+
}
222+
223+
// Add blinding point if present (only for introduction node).
224+
if len(hop.BlindingPoint) > 0 {
225+
pubKey, err := btcec.ParsePubKey(hop.BlindingPoint)
226+
if err != nil {
227+
return nil, fmt.Errorf("failed to parse "+
228+
"blinding point: %w", err)
229+
}
230+
routeHop.BlindingPoint = pubKey
231+
}
232+
233+
// Add encrypted data if present (for all blinded hops).
234+
if len(hop.EncryptedData) > 0 {
235+
routeHop.EncryptedData = hop.EncryptedData
236+
}
237+
238+
// Add total amount if present (only for final hop in blinded
239+
// route).
240+
if hop.BlindedPathTotalAmt.Valid {
241+
routeHop.TotalAmtMsat = lnwire.MilliSatoshi(
242+
hop.BlindedPathTotalAmt.Int64,
243+
)
244+
}
245+
246+
// Add hop-level custom records.
247+
if records, ok := hopCustomRecords[hop.ID]; ok {
248+
routeHop.CustomRecords = make(
249+
record.CustomSet,
250+
)
251+
for _, rec := range records {
252+
routeHop.CustomRecords[uint64(rec.Key)] =
253+
rec.Value
254+
}
255+
}
256+
257+
// Add metadata if present.
258+
if len(hop.MetaData) > 0 {
259+
routeHop.Metadata = hop.MetaData
260+
}
261+
262+
routeHops[i] = routeHop
263+
}
264+
265+
// Parse the source node public key.
266+
var sourceNode route.Vertex
267+
copy(sourceNode[:], sourceKey)
268+
269+
route := &route.Route{
270+
TotalTimeLock: uint32(totalTimeLock),
271+
TotalAmount: lnwire.MilliSatoshi(totalAmount),
272+
SourcePubKey: sourceNode,
273+
Hops: routeHops,
274+
FirstHopWireCustomRecords: firstHopWireCustomRecords,
275+
}
276+
277+
// Set the first hop amount if it is set.
278+
if firstHopAmountMsat != 0 {
279+
route.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
280+
tlv.NewBigSizeT(lnwire.MilliSatoshi(
281+
firstHopAmountMsat,
282+
)),
283+
)
284+
}
285+
286+
return route, nil
287+
}

0 commit comments

Comments
 (0)