From df7dfba12ab291eeff962364726a3a313956eea4 Mon Sep 17 00:00:00 2001 From: Gustavo Stingelin Date: Wed, 8 Jul 2026 23:35:40 -0300 Subject: [PATCH] staticaddr/loopin: preserve loop-in last update time --- staticaddr/loopin/sql_store.go | 31 +++++++++++++++++++++++++---- staticaddr/loopin/sql_store_test.go | 15 +++++++++----- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/staticaddr/loopin/sql_store.go b/staticaddr/loopin/sql_store.go index 8b36dbe4..9dc2a084 100644 --- a/staticaddr/loopin/sql_store.go +++ b/staticaddr/loopin/sql_store.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "strings" + "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcutil" @@ -23,6 +24,12 @@ import ( const OutpointSeparator = ";" +// sqlStoreUpdateTime returns the PostgreSQL-compatible timestamp precision used +// for persisted loop-in update metadata. +func sqlStoreUpdateTime(clock clock.Clock) time.Time { + return clock.Now().Truncate(time.Microsecond) +} + var ( // ErrInvalidOutpoint is returned when an outpoint contains the outpoint // separator. @@ -288,13 +295,14 @@ func (s *SqlStore) CreateLoopIn(ctx context.Context, Fast: loopIn.Fast, } + updateTime := sqlStoreUpdateTime(s.clock) updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{ SwapHash: loopIn.SwapHash[:], - UpdateTimestamp: s.clock.Now(), + UpdateTimestamp: updateTime, UpdateState: string(loopIn.GetState()), } - return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), + err := s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), func(q Querier) error { err := q.InsertSwap(ctx, swapArgs) if err != nil { @@ -331,6 +339,13 @@ func (s *SqlStore) CreateLoopIn(ctx context.Context, return q.InsertStaticAddressMetaUpdate(ctx, updateArgs) }, ) + if err != nil { + return err + } + + loopIn.LastUpdateTime = updateTime + + return nil } // UpdateLoopIn updates the loop-in in the database. @@ -351,13 +366,14 @@ func (s *SqlStore) UpdateLoopIn(ctx context.Context, }, } + updateTime := sqlStoreUpdateTime(s.clock) updateArgs := sqlc.InsertStaticAddressMetaUpdateParams{ SwapHash: loopIn.SwapHash[:], UpdateState: string(loopIn.GetState()), - UpdateTimestamp: s.clock.Now(), + UpdateTimestamp: updateTime, } - return s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), + err := s.baseDB.ExecTx(ctx, loopdb.NewSqlWriteOpts(), func(q Querier) error { err := q.UpdateStaticAddressLoopIn(ctx, updateParams) if err != nil { @@ -367,6 +383,13 @@ func (s *SqlStore) UpdateLoopIn(ctx context.Context, return q.InsertStaticAddressMetaUpdate(ctx, updateArgs) }, ) + if err != nil { + return err + } + + loopIn.LastUpdateTime = updateTime + + return nil } // RecordStaticAddressRiskDecision stores the server's confirmation-risk diff --git a/staticaddr/loopin/sql_store_test.go b/staticaddr/loopin/sql_store_test.go index ffea0f06..c08d940f 100644 --- a/staticaddr/loopin/sql_store_test.go +++ b/staticaddr/loopin/sql_store_test.go @@ -252,7 +252,9 @@ func TestCreateLoopIn(t *testing.T) { // Set up test context objects. ctx := t.Context() testDb := loopdb.NewTestDB(t) - testClock := clock.NewTestClock(time.Now()) + createTime := time.Unix(1_717_171_717, 123_456_789).UTC() + expectedCreateTime := createTime.Truncate(time.Microsecond) + testClock := clock.NewTestClock(createTime) defer testDb.Close() depositStore := deposit.NewSqlStore(testDb.BaseDB) @@ -325,6 +327,7 @@ func TestCreateLoopIn(t *testing.T) { err = swapStore.CreateLoopIn(ctx, &swapPending) require.NoError(t, err) + require.Equal(t, expectedCreateTime, swapPending.LastUpdateTime) depositIDs, err := swapStore.DepositIDsForSwapHash( ctx, swapHashPending, @@ -349,6 +352,7 @@ func TestCreateLoopIn(t *testing.T) { require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()}, swap.DepositOutpoints) require.Equal(t, SignHtlcTx, swap.GetState()) + require.Equal(t, swapPending.LastUpdateTime, swap.LastUpdateTime) require.Equal( t, ConfirmationRiskDecisionNone, swap.ConfirmationRiskDecision, @@ -445,14 +449,15 @@ func TestCreateLoopIn(t *testing.T) { err = swapStore.UpdateLoopIn(ctx, &swapPending) require.NoError(t, err) + require.Equal( + t, updateTime.Truncate(time.Microsecond), + swapPending.LastUpdateTime, + ) swap, err = swapStore.GetLoopInByHash(ctx, swapHashPending) require.NoError(t, err) require.Equal(t, Succeeded, swap.GetState()) - require.WithinDuration( - t, updateTime.UTC(), swap.LastUpdateTime.UTC(), - time.Microsecond, - ) + require.Equal(t, swapPending.LastUpdateTime, swap.LastUpdateTime) } // TestGetLoopInByHashOrdersDepositsBySnapshot ensures recovered deposits are