mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Merge pull request #682 from sputn1ck/fix_faulty_timestamps
Fix faulty timestamps
This commit is contained in:
commit
9f2bf5ca07
2 changed files with 17 additions and 203 deletions
|
|
@ -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)
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
133
loopdb/sqlite.go
133
loopdb/sqlite.go
|
|
@ -3,7 +3,6 @@ package loopdb
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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"`
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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(
|
||||
|
|
@ -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, "-")
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue