Skip to content

Commit 824f543

Browse files
authored
Merge pull request #617 from sputn1ck/fix_faulty_timestamps
loopdb: fix faulty timestamps on startup
2 parents 92c1f8c + be9ca71 commit 824f543

File tree

6 files changed

+330
-32
lines changed

6 files changed

+330
-32
lines changed

loopd/swapclient_server.go

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -156,21 +156,22 @@ func (s *swapClientServer) LoopOut(ctx context.Context,
156156
return nil, err
157157
}
158158

159+
// Infer if the publication deadline is set in milliseconds.
160+
publicationDeadline := getPublicationDeadline(in.SwapPublicationDeadline)
161+
159162
req := &loop.OutRequest{
160-
Amount: btcutil.Amount(in.Amt),
161-
DestAddr: sweepAddr,
162-
MaxMinerFee: btcutil.Amount(in.MaxMinerFee),
163-
MaxPrepayAmount: btcutil.Amount(in.MaxPrepayAmt),
164-
MaxPrepayRoutingFee: btcutil.Amount(in.MaxPrepayRoutingFee),
165-
MaxSwapRoutingFee: btcutil.Amount(in.MaxSwapRoutingFee),
166-
MaxSwapFee: btcutil.Amount(in.MaxSwapFee),
167-
SweepConfTarget: sweepConfTarget,
168-
HtlcConfirmations: in.HtlcConfirmations,
169-
SwapPublicationDeadline: time.Unix(
170-
int64(in.SwapPublicationDeadline), 0,
171-
),
172-
Label: in.Label,
173-
Initiator: in.Initiator,
163+
Amount: btcutil.Amount(in.Amt),
164+
DestAddr: sweepAddr,
165+
MaxMinerFee: btcutil.Amount(in.MaxMinerFee),
166+
MaxPrepayAmount: btcutil.Amount(in.MaxPrepayAmt),
167+
MaxPrepayRoutingFee: btcutil.Amount(in.MaxPrepayRoutingFee),
168+
MaxSwapRoutingFee: btcutil.Amount(in.MaxSwapRoutingFee),
169+
MaxSwapFee: btcutil.Amount(in.MaxSwapFee),
170+
SweepConfTarget: sweepConfTarget,
171+
HtlcConfirmations: in.HtlcConfirmations,
172+
SwapPublicationDeadline: publicationDeadline,
173+
Label: in.Label,
174+
Initiator: in.Initiator,
174175
}
175176

176177
switch {
@@ -538,13 +539,14 @@ func (s *swapClientServer) LoopOutQuote(ctx context.Context,
538539
if err != nil {
539540
return nil, err
540541
}
542+
543+
publicactionDeadline := getPublicationDeadline(req.SwapPublicationDeadline)
544+
541545
quote, err := s.impl.LoopOutQuote(ctx, &loop.LoopOutQuoteRequest{
542-
Amount: btcutil.Amount(req.Amt),
543-
SweepConfTarget: confTarget,
544-
SwapPublicationDeadline: time.Unix(
545-
int64(req.SwapPublicationDeadline), 0,
546-
),
547-
Initiator: defaultLoopdInitiator,
546+
Amount: btcutil.Amount(req.Amt),
547+
SweepConfTarget: confTarget,
548+
SwapPublicationDeadline: publicactionDeadline,
549+
Initiator: defaultLoopdInitiator,
548550
})
549551
if err != nil {
550552
return nil, err
@@ -1249,3 +1251,19 @@ func hasBandwidth(channels []lndclient.ChannelInfo, amt btcutil.Amount,
12491251

12501252
return false, 0
12511253
}
1254+
1255+
// getPublicationDeadline returns the publication deadline for a swap given the
1256+
// unix timestamp. If the timestamp is believed to be in milliseconds, then it
1257+
// is converted to seconds.
1258+
func getPublicationDeadline(unixTimestamp uint64) time.Time {
1259+
length := len(fmt.Sprintf("%d", unixTimestamp))
1260+
if length >= 13 {
1261+
// Likely a millisecond timestamp
1262+
secs := unixTimestamp / 1000
1263+
nsecs := (unixTimestamp % 1000) * 1e6
1264+
return time.Unix(int64(secs), int64(nsecs))
1265+
} else {
1266+
// Likely a second timestamp
1267+
return time.Unix(int64(unixTimestamp), 0)
1268+
}
1269+
}

loopdb/postgres.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package loopdb
22

33
import (
4+
"context"
45
"database/sql"
56
"fmt"
67
"testing"
@@ -104,13 +105,25 @@ func NewPostgresStore(cfg *PostgresConfig,
104105

105106
queries := sqlc.New(rawDb)
106107

108+
baseDB := &BaseDB{
109+
DB: rawDb,
110+
Queries: queries,
111+
network: network,
112+
}
113+
114+
// Fix faulty timestamps in the database.
115+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
116+
defer cancel()
117+
118+
err = baseDB.FixFaultyTimestamps(ctx, parsePostgresTimeStamp)
119+
if err != nil {
120+
log.Errorf("Failed to fix faulty timestamps: %v", err)
121+
return nil, err
122+
}
123+
107124
return &PostgresStore{
108-
cfg: cfg,
109-
BaseDB: &BaseDB{
110-
DB: rawDb,
111-
Queries: queries,
112-
network: network,
113-
},
125+
cfg: cfg,
126+
BaseDB: baseDB,
114127
}, nil
115128
}
116129

loopdb/sql_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,76 @@ func TestSqliteTypeConversion(t *testing.T) {
318318

319319
}
320320

321+
// TestIssue615 tests that on faulty timestamps, the database will be fixed.
322+
// Reference: https://github.com/lightninglabs/lightning-terminal/issues/615
323+
func TestIssue615(t *testing.T) {
324+
ctxb := context.Background()
325+
326+
// Create a new sqlite store for testing.
327+
sqlDB := NewTestDB(t)
328+
329+
// Create a faulty loopout swap.
330+
destAddr := test.GetDestAddr(t, 0)
331+
faultyTime, err := parseSqliteTimeStamp("55563-06-27 02:09:24 +0000 UTC")
332+
require.NoError(t, err)
333+
334+
unrestrictedSwap := LoopOutContract{
335+
SwapContract: SwapContract{
336+
AmountRequested: 100,
337+
Preimage: testPreimage,
338+
CltvExpiry: 144,
339+
HtlcKeys: HtlcKeys{
340+
SenderScriptKey: senderKey,
341+
ReceiverScriptKey: receiverKey,
342+
SenderInternalPubKey: senderInternalKey,
343+
ReceiverInternalPubKey: receiverInternalKey,
344+
ClientScriptKeyLocator: keychain.KeyLocator{
345+
Family: 1,
346+
Index: 2,
347+
},
348+
},
349+
MaxMinerFee: 10,
350+
MaxSwapFee: 20,
351+
InitiationHeight: 99,
352+
InitiationTime: time.Now(),
353+
ProtocolVersion: ProtocolVersionMuSig2,
354+
},
355+
MaxPrepayRoutingFee: 40,
356+
PrepayInvoice: "prepayinvoice",
357+
DestAddr: destAddr,
358+
SwapInvoice: "swapinvoice",
359+
MaxSwapRoutingFee: 30,
360+
SweepConfTarget: 2,
361+
HtlcConfirmations: 2,
362+
SwapPublicationDeadline: faultyTime,
363+
}
364+
365+
err = sqlDB.CreateLoopOut(ctxb, testPreimage.Hash(), &unrestrictedSwap)
366+
require.NoError(t, err)
367+
368+
// This should fail because of the faulty timestamp.
369+
_, err = sqlDB.GetLoopOutSwaps(ctxb)
370+
371+
// If we're using sqlite, we expect an error.
372+
if testDBType == "sqlite" {
373+
require.Error(t, err)
374+
} else {
375+
require.NoError(t, err)
376+
}
377+
378+
parseFunc := parseSqliteTimeStamp
379+
if testDBType == "postgres" {
380+
parseFunc = parsePostgresTimeStamp
381+
}
382+
383+
// Fix the faulty timestamp.
384+
err = sqlDB.FixFaultyTimestamps(ctxb, parseFunc)
385+
require.NoError(t, err)
386+
387+
_, err = sqlDB.GetLoopOutSwaps(ctxb)
388+
require.NoError(t, err)
389+
}
390+
321391
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
322392

323393
func randomString(length int) string {

0 commit comments

Comments
 (0)