Skip to content

Commit bd8897b

Browse files
committed
paymentsdb: implement QueryPayments for sql backend
1 parent e33d0ba commit bd8897b

File tree

3 files changed

+726
-2
lines changed

3 files changed

+726
-2
lines changed

payments/db/sql_converters.go

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
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+
hash, err := lntypes.MakeHash(dbAttempt.PaymentHash)
68+
if err != nil {
69+
return nil, fmt.Errorf("failed to parse payment "+
70+
"hash: %w", err)
71+
}
72+
73+
// Create the attempt info.
74+
var sessionKey [32]byte
75+
copy(sessionKey[:], dbAttempt.SessionKey)
76+
77+
info := HTLCAttemptInfo{
78+
AttemptID: uint64(dbAttempt.AttemptIndex),
79+
sessionKey: sessionKey,
80+
Route: *route,
81+
AttemptTime: dbAttempt.AttemptTime,
82+
Hash: &hash,
83+
}
84+
85+
attempt := &HTLCAttempt{
86+
HTLCAttemptInfo: info,
87+
}
88+
89+
// Add settlement info if present.
90+
if dbAttempt.ResolutionType.Valid &&
91+
HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
92+
HTLCAttemptResolutionSettled {
93+
94+
var preimage lntypes.Preimage
95+
copy(preimage[:], dbAttempt.SettlePreimage)
96+
97+
settleTime := dbAttempt.ResolutionTime.Time
98+
if st, ok := dbAttempt.SettleTime.(time.Time); ok {
99+
settleTime = st
100+
}
101+
102+
attempt.Settle = &HTLCSettleInfo{
103+
Preimage: preimage,
104+
SettleTime: settleTime,
105+
}
106+
}
107+
108+
// Add failure info if present.
109+
if dbAttempt.ResolutionType.Valid &&
110+
HTLCAttemptResolutionType(dbAttempt.ResolutionType.Int32) ==
111+
HTLCAttemptResolutionFailed {
112+
113+
failTime := dbAttempt.ResolutionTime.Time
114+
if ft, ok := dbAttempt.FailTime.(time.Time); ok {
115+
failTime = ft
116+
}
117+
118+
failure := &HTLCFailInfo{
119+
FailTime: failTime,
120+
}
121+
122+
if dbAttempt.HtlcFailReason.Valid {
123+
failure.Reason = HTLCFailReason(
124+
dbAttempt.HtlcFailReason.Int32,
125+
)
126+
}
127+
128+
if dbAttempt.FailureSourceIndex.Valid {
129+
failure.FailureSourceIndex = uint32(
130+
dbAttempt.FailureSourceIndex.Int32,
131+
)
132+
}
133+
134+
// Decode the failure message if present.
135+
if len(dbAttempt.FailureMsg) > 0 {
136+
msg, err := lnwire.DecodeFailureMessage(
137+
bytes.NewReader(dbAttempt.FailureMsg), 0,
138+
)
139+
if err != nil {
140+
return nil, fmt.Errorf("failed to decode "+
141+
"failure message: %w", err)
142+
}
143+
failure.Message = msg
144+
}
145+
146+
attempt.Failure = failure
147+
}
148+
149+
return attempt, nil
150+
}
151+
152+
// dbDataToRoute converts database route data to a route.Route.
153+
func dbDataToRoute(hops []sqlc.FetchHopsForAttemptsRow,
154+
hopCustomRecords map[int64][]sqlc.PaymentHopCustomRecord,
155+
firstHopAmountMsat int64, totalTimeLock int32, totalAmount int64,
156+
sourceKey []byte, firstHopWireCustomRecords lnwire.CustomRecords) (
157+
*route.Route, error) {
158+
159+
if len(hops) == 0 {
160+
return nil, fmt.Errorf("no hops provided")
161+
}
162+
163+
// Sort hops by hop index.
164+
sort.Slice(hops, func(i, j int) bool {
165+
return hops[i].HopIndex < hops[j].HopIndex
166+
})
167+
168+
routeHops := make([]*route.Hop, len(hops))
169+
170+
for i, hop := range hops {
171+
pubKey, err := route.NewVertexFromBytes(hop.PubKey)
172+
if err != nil {
173+
return nil, fmt.Errorf("failed to parse pub key: %w",
174+
err)
175+
}
176+
177+
var channelID uint64
178+
if hop.Scid != "" {
179+
// The SCID is stored as a string representation
180+
// of the uint64.
181+
_, err := fmt.Sscanf(hop.Scid, "%d", &channelID)
182+
if err != nil {
183+
return nil, fmt.Errorf("failed to parse "+
184+
"scid: %w", err)
185+
}
186+
}
187+
188+
routeHop := &route.Hop{
189+
PubKeyBytes: pubKey,
190+
ChannelID: channelID,
191+
OutgoingTimeLock: uint32(hop.OutgoingTimeLock),
192+
AmtToForward: lnwire.MilliSatoshi(hop.AmtToForward),
193+
}
194+
195+
// Add MPP record if present.
196+
if len(hop.MppPaymentAddr) > 0 {
197+
var paymentAddr [32]byte
198+
copy(paymentAddr[:], hop.MppPaymentAddr)
199+
routeHop.MPP = record.NewMPP(
200+
lnwire.MilliSatoshi(hop.MppTotalMsat.Int64),
201+
paymentAddr,
202+
)
203+
}
204+
205+
// Add AMP record if present.
206+
if len(hop.AmpRootShare) > 0 {
207+
var rootShare [32]byte
208+
copy(rootShare[:], hop.AmpRootShare)
209+
var setID [32]byte
210+
copy(setID[:], hop.AmpSetID)
211+
212+
routeHop.AMP = record.NewAMP(
213+
rootShare, setID,
214+
uint32(hop.AmpChildIndex.Int32),
215+
)
216+
}
217+
218+
// Add blinding point if present (only for introduction node).
219+
if len(hop.BlindingPoint) > 0 {
220+
pubKey, err := btcec.ParsePubKey(hop.BlindingPoint)
221+
if err != nil {
222+
return nil, fmt.Errorf("failed to parse "+
223+
"blinding point: %w", err)
224+
}
225+
routeHop.BlindingPoint = pubKey
226+
}
227+
228+
// Add encrypted data if present (for all blinded hops).
229+
if len(hop.EncryptedData) > 0 {
230+
routeHop.EncryptedData = hop.EncryptedData
231+
}
232+
233+
// Add total amount if present (only for final hop in blinded
234+
// route).
235+
if hop.BlindedPathTotalAmt.Valid {
236+
routeHop.TotalAmtMsat = lnwire.MilliSatoshi(
237+
hop.BlindedPathTotalAmt.Int64,
238+
)
239+
}
240+
241+
// Add hop-level custom records.
242+
if records, ok := hopCustomRecords[hop.ID]; ok {
243+
routeHop.CustomRecords = make(
244+
record.CustomSet,
245+
)
246+
for _, rec := range records {
247+
routeHop.CustomRecords[uint64(rec.Key)] =
248+
rec.Value
249+
}
250+
}
251+
252+
// Add metadata if present.
253+
if len(hop.MetaData) > 0 {
254+
routeHop.Metadata = hop.MetaData
255+
}
256+
257+
routeHops[i] = routeHop
258+
}
259+
260+
// Parse the source node public key.
261+
var sourceNode route.Vertex
262+
copy(sourceNode[:], sourceKey)
263+
264+
route := &route.Route{
265+
TotalTimeLock: uint32(totalTimeLock),
266+
TotalAmount: lnwire.MilliSatoshi(totalAmount),
267+
SourcePubKey: sourceNode,
268+
Hops: routeHops,
269+
FirstHopWireCustomRecords: firstHopWireCustomRecords,
270+
}
271+
272+
// Set the first hop amount if it is set.
273+
if firstHopAmountMsat != 0 {
274+
route.FirstHopAmount = tlv.NewRecordT[tlv.TlvType0](
275+
tlv.NewBigSizeT(lnwire.MilliSatoshi(
276+
firstHopAmountMsat,
277+
)),
278+
)
279+
}
280+
281+
return route, nil
282+
}

0 commit comments

Comments
 (0)