Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 10 additions & 77 deletions loopdb/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,15 @@ func TestSqliteTypeConversion(t *testing.T) {
func TestIssue615(t *testing.T) {
ctxb := context.Background()

// Create an invoice to get the timestamp from.
invoice := "lnbc5u1pje2dyusp5qs356crpns9u3we8hw7w9gntfz89zkcaxu6w6h6a" +
"pw6jlgc0cynqpp5y2xdzu4eqasuttxp3nrk72vqdzce3wead7nmf693uqpgx" +
"2hd533qdpcyfnx2etyyp3ks6trddjkuueqw3hkketwwv7kgvrd0py95d6vvv" +
"65z0fzxqzfvcqpjrzjqd82srutzjx82prr234anxdlwvs6peklcc92lp9aqs" +
"q296xnwmqd2rrf9gqqtwqqqqqqqqqqqqqqqqqq9q9qxpqysgq768236z7cx6" +
"gyy766wajrmpnpt6wavkf5nypwyj6r3dcxm89aggq2jm2kznaxvr0lrsqgv7" +
"592upfh5ruyrwzy5tethpzere78xfgwqp64jrpa"

// Create a new sqlite store for testing.
sqlDB := NewTestDB(t)

Expand Down Expand Up @@ -356,7 +365,7 @@ func TestIssue615(t *testing.T) {
MaxPrepayRoutingFee: 40,
PrepayInvoice: "prepayinvoice",
DestAddr: destAddr,
SwapInvoice: "swapinvoice",
SwapInvoice: invoice,
MaxSwapRoutingFee: 30,
SweepConfTarget: 2,
HtlcConfirmations: 2,
Expand Down Expand Up @@ -384,82 +393,6 @@ func TestIssue615(t *testing.T) {
require.NoError(t, err)
}

func TestTimeConversions(t *testing.T) {
tests := []struct {
timeString string
expectedTime time.Time
}{
{
timeString: "2018-11-01 00:00:00 +0000 UTC",
expectedTime: time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC),
},
{
timeString: "2018-11-01 00:00:01.10000 +0000 UTC",
expectedTime: time.Date(2018, 11, 1, 0, 0, 1, 100000000, time.UTC),
},
{
timeString: "2053-12-29T02:40:44.269009408Z",
expectedTime: time.Date(
time.Now().Year(), 12, 29, 2, 40, 44, 269009408, time.UTC,
),
},
{
timeString: "55563-06-27 02:09:24 +0000 UTC",
expectedTime: time.Date(
time.Now().Year(), 6, 27, 2, 9, 24, 0, time.UTC,
),
},
{
timeString: "2172-03-11 10:01:11.849906176 +0000 UTC",
expectedTime: time.Date(
time.Now().Year(), 3, 11, 10, 1, 11, 849906176, time.UTC,
),
},
{
timeString: "2023-08-04 16:07:49 +0800 CST",
expectedTime: time.Date(
2023, 8, 4, 8, 7, 49, 0, time.UTC,
),
},
{
timeString: "2023-08-04 16:07:49 -0700 MST",
expectedTime: time.Date(
2023, 8, 4, 23, 7, 49, 0, time.UTC,
),
},
{
timeString: "2023-08-04T16:07:49+08:00",
expectedTime: time.Date(
2023, 8, 4, 8, 7, 49, 0, time.UTC,
),
},
{
timeString: "2023-08-04T16:07:49+08:00",
expectedTime: time.Date(
2023, 8, 4, 8, 7, 49, 0, time.UTC,
),
},
{
timeString: "2188-02-29 15:34:23.847906176 +0000 UTC",
expectedTime: time.Date(
2023, 2, 28, 15, 34, 23, 847906176, time.UTC,
),
},
{
timeString: "2188-02-29T16:07:49+08:00",
expectedTime: time.Date(
2023, 2, 28, 8, 7, 49, 0, time.UTC,
),
},
}

for _, test := range tests {
time, err := fixTimeStamp(test.timeString)
require.NoError(t, err)
require.Equal(t, test.expectedTime, time)
}
}

const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func randomString(length int) string {
Expand Down
133 changes: 7 additions & 126 deletions loopdb/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package loopdb
import (
"context"
"database/sql"
"errors"
"fmt"
"net/url"
"path/filepath"
Expand All @@ -15,6 +14,7 @@ import (
"github.com/btcsuite/btcd/chaincfg"
sqlite_migrate "github.com/golang-migrate/migrate/v4/database/sqlite"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightningnetwork/lnd/zpay32"

"github.com/stretchr/testify/require"
_ "modernc.org/sqlite" // Register relevant drivers.
Expand Down Expand Up @@ -213,7 +213,7 @@ func (db *BaseDB) ExecTx(ctx context.Context, txOptions TxOptions,
func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {
// Manually fetch all the loop out swaps.
rows, err := b.DB.QueryContext(
ctx, "SELECT swap_hash, publication_deadline FROM loopout_swaps",
ctx, "SELECT swap_hash, swap_invoice, publication_deadline FROM loopout_swaps",
)
if err != nil {
return err
Expand All @@ -226,6 +226,7 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {
// the sqlite driver will fail on faulty timestamps.
type LoopOutRow struct {
Hash []byte `json:"swap_hash"`
SwapInvoice string `json:"swap_invoice"`
PublicationDeadline string `json:"publication_deadline"`
}

Expand All @@ -234,7 +235,7 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {
for rows.Next() {
var swap LoopOutRow
err := rows.Scan(
&swap.Hash, &swap.PublicationDeadline,
&swap.Hash, &swap.SwapInvoice, &swap.PublicationDeadline,
)
if err != nil {
return err
Expand Down Expand Up @@ -264,14 +265,15 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {

// Skip if the year is not in the future.
thisYear := time.Now().Year()
if year <= thisYear {
if year > 2020 && year <= thisYear {
continue
}

fixedTime, err := fixTimeStamp(swap.PublicationDeadline)
payReq, err := zpay32.Decode(swap.SwapInvoice, b.network)
if err != nil {
return err
}
fixedTime := payReq.Timestamp.Add(time.Minute * 30)

// Update the faulty time to a valid time.
_, err = tx.ExecContext(
Expand Down Expand Up @@ -322,92 +324,6 @@ func (r *SqliteTxOptions) ReadOnly() bool {
return r.readOnly
}

// fixTimeStamp tries to parse a timestamp string with both the
// parseSqliteTimeStamp and parsePostgresTimeStamp functions.
// If both fail, it returns an error.
func fixTimeStamp(dateTimeStr string) (time.Time, error) {
year, err := getTimeStampYear(dateTimeStr)
if err != nil {
return time.Time{}, err
}

// If the year is in the future. It was a faulty timestamp.
thisYear := time.Now().Year()
if year > thisYear {
dateTimeStr = strings.Replace(
dateTimeStr,
fmt.Sprintf("%d", year),
fmt.Sprintf("%d", thisYear),
1,
)
}

// If the year is a leap year and the date is 29th of February, we
// need to change it to 28th of February. Otherwise, the time.Parse
// function will fail, as a non-leap year cannot have 29th of February.
day, month, err := extractDayAndMonth(dateTimeStr)
if err != nil {
return time.Time{}, fmt.Errorf("unable to parse timestamp day "+
"and month %v: %v", dateTimeStr, err)
}

if !isLeapYear(thisYear) &&
month == 2 && day == 29 {

dateTimeStr = strings.Replace(
dateTimeStr,
fmt.Sprintf("%d-02-29", thisYear),
fmt.Sprintf("%d-02-28", thisYear),
1,
)
}

parsedTime, err := parseLayouts(defaultLayouts(), dateTimeStr)
if err != nil {
return time.Time{}, fmt.Errorf("unable to parse timestamp %v: %v",
dateTimeStr, err)
}

return parsedTime.UTC(), nil
}

// parseLayouts parses time based on a list of provided layouts.
// If layouts is empty list or nil, the error with unknown layout will be returned.
func parseLayouts(layouts []string, dateTime string) (time.Time, error) {
for _, layout := range layouts {
parsedTime, err := time.Parse(layout, dateTime)
if err == nil {
return parsedTime, nil
}
}

return time.Time{}, errors.New("unknown layout")
}

// defaultLayouts returns a default list of ALL supported layouts.
// This function returns new copy of a slice.
func defaultLayouts() []string {
return []string{
"2006-01-02 15:04:05.99999 -0700 MST", // Custom sqlite layout.
time.RFC3339Nano,
time.RFC3339,
time.RFC1123Z,
time.RFC1123,
time.RFC850,
time.RFC822Z,
time.RFC822,
time.Layout,
time.RubyDate,
time.UnixDate,
time.ANSIC,
time.StampNano,
time.StampMicro,
time.StampMilli,
time.Stamp,
time.Kitchen,
}
}

// getTimeStampYear returns the year of a timestamp string.
func getTimeStampYear(dateTimeStr string) (int, error) {
parts := strings.Split(dateTimeStr, "-")
Expand All @@ -423,38 +339,3 @@ func getTimeStampYear(dateTimeStr string) (int, error) {

return year, nil
}

// extractDayAndMonth extracts the day and month from a date string.
func extractDayAndMonth(dateStr string) (int, int, error) {
// Split the date string into parts using various delimiters.
parts := strings.FieldsFunc(dateStr, func(r rune) bool {
return r == '-' || r == ' ' || r == 'T' || r == ':' || r == '+' || r == 'Z'
})

if len(parts) < 3 {
return 0, 0, fmt.Errorf("Invalid date format: %s", dateStr)
}

// Extract year, month, and day from the parts.
_, err := strconv.Atoi(parts[0])
if err != nil {
return 0, 0, err
}

month, err := strconv.Atoi(parts[1])
if err != nil {
return 0, 0, err
}

day, err := strconv.Atoi(parts[2])
if err != nil {
return 0, 0, err
}

return day, month, nil
}

// isLeapYear returns true if the year is a leap year.
func isLeapYear(year int) bool {
return (year%4 == 0 && year%100 != 0) || (year%400 == 0)
}