Skip to content

Commit 353160c

Browse files
committed
paymentsdb: implement QueryPayments for sql backend
1 parent 52d466f commit 353160c

File tree

3 files changed

+740
-0
lines changed

3 files changed

+740
-0
lines changed

payments/db/sql_converters.go

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

0 commit comments

Comments
 (0)