Skip to content

Commit b702aeb

Browse files
committed
multi: inject clocks into swap lifecycle and update tests
Inject clock.Clock into swapConfig so that swap initiation times are deterministic and testable. Update all swap tests to use TestClock with a fixed time, and assert that InitiationTime matches the clock value.
1 parent 66a17f4 commit b702aeb

8 files changed

Lines changed: 63 additions & 19 deletions

File tree

client.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/lightninglabs/loop/sweepbatcher"
2323
"github.com/lightninglabs/loop/utils"
2424
"github.com/lightninglabs/taproot-assets/rpcutils"
25+
"github.com/lightningnetwork/lnd/clock"
2526
"github.com/lightningnetwork/lnd/lntypes"
2627
"github.com/lightningnetwork/lnd/routing/route"
2728
"google.golang.org/grpc"
@@ -485,7 +486,10 @@ func (s *Client) Run(ctx context.Context, statusChan chan<- SwapInfo) error {
485486
func (s *Client) resumeSwaps(ctx context.Context,
486487
loopOutSwaps []*loopdb.LoopOut, loopInSwaps []*loopdb.LoopIn) {
487488

488-
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.AssetClient)
489+
swapCfg := newSwapConfig(
490+
s.lndServices, s.Store, s.Server, s.AssetClient,
491+
clock.NewDefaultClock(),
492+
)
489493

490494
for _, pend := range loopOutSwaps {
491495
if pend.State().State.Type() != loopdb.StateTypePending {
@@ -578,6 +582,7 @@ func (s *Client) LoopOut(globalCtx context.Context,
578582
// Create a new swap object for this swap.
579583
swapCfg := newSwapConfig(
580584
s.lndServices, s.Store, s.Server, s.AssetClient,
585+
clock.NewDefaultClock(),
581586
)
582587

583588
initResult, err := newLoopOutSwap(
@@ -759,7 +764,10 @@ func (s *Client) LoopIn(globalCtx context.Context,
759764

760765
// Create a new swap object for this swap.
761766
initiationHeight := s.executor.height()
762-
swapCfg := newSwapConfig(s.lndServices, s.Store, s.Server, s.AssetClient)
767+
swapCfg := newSwapConfig(
768+
s.lndServices, s.Store, s.Server, s.AssetClient,
769+
clock.NewDefaultClock(),
770+
)
763771
initResult, err := newLoopInSwap(
764772
globalCtx, swapCfg, initiationHeight, request,
765773
)

cost_migration_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/lightninglabs/lndclient"
1010
"github.com/lightninglabs/loop/loopdb"
1111
"github.com/lightninglabs/loop/test"
12+
"github.com/lightningnetwork/lnd/clock"
1213
"github.com/lightningnetwork/lnd/lntypes"
1314
"github.com/lightningnetwork/lnd/lnwire"
1415
"github.com/stretchr/testify/require"
@@ -25,6 +26,7 @@ func TestCalculateLoopOutCost(t *testing.T) {
2526
lnd: &lnd.LndServices,
2627
store: store,
2728
server: server,
29+
clock: clock.NewTestClock(time.Unix(123, 0)),
2830
}
2931

3032
height := int32(600)
@@ -118,6 +120,7 @@ func TestCostMigration(t *testing.T) {
118120
lnd: &lnd.LndServices,
119121
store: store,
120122
server: server,
123+
clock: clock.NewTestClock(time.Unix(123, 0)),
121124
}
122125

123126
height := int32(600)

loopd/daemon.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,7 +1005,8 @@ func (d *Daemon) initialize(withMacaroonService bool) error {
10051005
}
10061006

10071007
loop.Resume(
1008-
d.mainCtx, notificationManager, swapClient.Store, d.impl.Conn, d.lnd,
1008+
d.mainCtx, notificationManager, swapClient.Store,
1009+
d.impl.Conn, d.lnd, clock.NewDefaultClock(),
10091010
)
10101011

10111012
// Last, start our internal error handler. This will return exactly one

loopin.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"errors"
88
"fmt"
99
"sync"
10-
"time"
1110

1211
"github.com/btcsuite/btcd/btcec/v2"
1312
"github.com/btcsuite/btcd/btcutil"
@@ -264,7 +263,7 @@ func newLoopInSwap(globalCtx context.Context, cfg *swapConfig,
264263

265264
// Instantiate a struct that contains all required data to start the
266265
// swap.
267-
initiationTime := time.Now()
266+
initiationTime := cfg.clock.Now()
268267

269268
contract := loopdb.LoopInContract{
270269
HtlcConfTarget: request.HtlcConfTarget,
@@ -837,7 +836,7 @@ func (s *loopInSwap) publishOnChainHtlc(ctx context.Context) (bool, error) {
837836
// the fee for publishing the htlc.
838837
s.cost.Onchain = fee
839838

840-
s.lastUpdateTime = time.Now()
839+
s.lastUpdateTime = s.clock.Now()
841840
if err := s.persistState(ctx); err != nil {
842841
return false, fmt.Errorf("persist htlc tx: %v", err)
843842
}
@@ -1210,7 +1209,7 @@ func (s *loopInSwap) persistState(ctx context.Context) error {
12101209

12111210
// setState updates the swap state and last update timestamp.
12121211
func (s *loopInSwap) setState(state loopdb.SwapState) {
1213-
s.lastUpdateTime = time.Now()
1212+
s.lastUpdateTime = s.clock.Now()
12141213
s.state = state
12151214
}
12161215

loopin_test.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/lightninglabs/loop/test"
1414
"github.com/lightninglabs/loop/utils"
1515
"github.com/lightningnetwork/lnd/chainntnfs"
16+
"github.com/lightningnetwork/lnd/clock"
1617
invpkg "github.com/lightningnetwork/lnd/invoices"
1718
"github.com/lightningnetwork/lnd/lntypes"
1819
"github.com/lightningnetwork/lnd/routing/route"
@@ -129,8 +130,12 @@ func testLoopInSuccess(t *testing.T) {
129130
ctx := newLoopInTestContext(t)
130131

131132
height := int32(600)
133+
startTime := time.Unix(123, 0)
132134

133-
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
135+
cfg := newSwapConfig(
136+
&ctx.lnd.LndServices, ctx.store, ctx.server, nil,
137+
clock.NewTestClock(startTime),
138+
)
134139

135140
expectedLastHop := &route.Vertex{0x02}
136141

@@ -144,6 +149,7 @@ func testLoopInSuccess(t *testing.T) {
144149
require.NoError(t, err)
145150

146151
inSwap := initResult.swap
152+
require.Equal(t, startTime, inSwap.InitiationTime)
147153

148154
ctx.store.AssertLoopInStored()
149155

@@ -279,7 +285,10 @@ func testLoopInTimeout(t *testing.T, externalValue int64) {
279285

280286
height := int32(600)
281287

282-
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
288+
cfg := newSwapConfig(
289+
&ctx.lnd.LndServices, ctx.store, ctx.server, nil,
290+
clock.NewTestClock(time.Unix(123, 0)),
291+
)
283292

284293
req := testLoopInRequest
285294
if externalValue != 0 {
@@ -490,7 +499,10 @@ func testLoopInResume(t *testing.T, state loopdb.SwapState, expired bool,
490499
ctxb := context.Background()
491500

492501
ctx := newLoopInTestContext(t)
493-
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
502+
cfg := newSwapConfig(
503+
&ctx.lnd.LndServices, ctx.store, ctx.server, nil,
504+
clock.NewTestClock(time.Unix(123, 0)),
505+
)
494506

495507
// Create sender and receiver keys.
496508
_, senderPubKey := test.CreateKey(1)
@@ -845,7 +857,10 @@ func advanceToPublishedHtlc(t *testing.T, ctx *loopInTestContext) SwapInfo {
845857
func startNewLoopIn(t *testing.T, ctx *loopInTestContext, height int32) (
846858
*swapConfig, *loopInSwap, error) {
847859

848-
cfg := newSwapConfig(&ctx.lnd.LndServices, ctx.store, ctx.server, nil)
860+
cfg := newSwapConfig(
861+
&ctx.lnd.LndServices, ctx.store, ctx.server, nil,
862+
clock.NewTestClock(time.Unix(123, 0)),
863+
)
849864

850865
req := &testLoopInRequest
851866

loopout.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/lightninglabs/taproot-assets/fn"
2626
"github.com/lightninglabs/taproot-assets/rfqmsg"
2727
"github.com/lightningnetwork/lnd/chainntnfs"
28+
"github.com/lightningnetwork/lnd/clock"
2829
"github.com/lightningnetwork/lnd/lnrpc"
2930
"github.com/lightningnetwork/lnd/lntypes"
3031
paymentsdb "github.com/lightningnetwork/lnd/payments/db"
@@ -192,7 +193,7 @@ func newLoopOutSwap(globalCtx context.Context, cfg *swapConfig,
192193

193194
// Instantiate a struct that contains all required data to start the
194195
// swap.
195-
initiationTime := time.Now()
196+
initiationTime := cfg.clock.Now()
196197

197198
contract := loopdb.LoopOutContract{
198199
SwapInvoice: swapResp.swapInvoice,
@@ -633,7 +634,7 @@ func (s *loopOutSwap) executeSwap(globalCtx context.Context) error {
633634

634635
// persistState updates the swap state and sends out an update notification.
635636
func (s *loopOutSwap) persistState(ctx context.Context) error {
636-
updateTime := time.Now()
637+
updateTime := s.clock.Now()
637638

638639
s.lastUpdateTime = updateTime
639640

@@ -864,12 +865,12 @@ func (s *loopOutSwap) payInvoiceAsync(ctx context.Context,
864865
payCtx, cancel := context.WithCancel(ctx)
865866
defer cancel()
866867

867-
start := time.Now()
868+
start := s.clock.Now()
868869
paymentStatus, attempts, err := s.sendPaymentWithRetry(
869870
payCtx, hash, &req, maxRetries, routingPlugin, pluginType,
870871
)
871872

872-
dt := time.Since(start)
873+
dt := s.clock.Now().Sub(start)
873874
paymentSuccess := err == nil &&
874875
paymentStatus.State == lnrpc.Payment_SUCCEEDED
875876

@@ -1543,6 +1544,7 @@ type resumeManager struct {
15431544
swapStore loopdb.SwapStore
15441545
swapClient swapserverrpc.SwapServerClient
15451546
lnd *lndclient.GrpcLndServices
1547+
clock clock.Clock
15461548

15471549
reqChan chan *swapserverrpc.ServerUnfinishedSwapNotification
15481550
}
@@ -1552,13 +1554,14 @@ type resumeManager struct {
15521554
func Resume(ctx context.Context, ntfnManager NotificationManager,
15531555
swapStore loopdb.SwapStore,
15541556
swapClientConn *grpc.ClientConn,
1555-
lnd *lndclient.GrpcLndServices) {
1557+
lnd *lndclient.GrpcLndServices, clock clock.Clock) {
15561558

15571559
resumeManager := &resumeManager{
15581560
ntfnManager: ntfnManager,
15591561
swapStore: swapStore,
15601562
swapClient: swapserverrpc.NewSwapServerClient(swapClientConn),
15611563
lnd: lnd,
1564+
clock: clock,
15621565
reqChan: make(chan *swapserverrpc.ServerUnfinishedSwapNotification, 1),
15631566
}
15641567
go resumeManager.start(ctx)
@@ -1717,7 +1720,7 @@ func (m *resumeManager) resumeLoopOutPayment(ctx context.Context,
17171720
cost.Server = payResp.Value.ToSatoshis() - amtRequested
17181721
cost.Offchain = payResp.Fee.ToSatoshis()
17191722
// Payment succeeded.
1720-
updateTime := time.Now()
1723+
updateTime := m.clock.Now()
17211724

17221725
// Update state in store.
17231726
err = m.swapStore.UpdateLoopOut(

loopout_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/lightninglabs/loop/sweep"
1919
"github.com/lightninglabs/loop/sweepbatcher"
2020
"github.com/lightninglabs/loop/test"
21+
"github.com/lightningnetwork/lnd/clock"
2122
"github.com/lightningnetwork/lnd/lnrpc"
2223
"github.com/lightningnetwork/lnd/lntypes"
2324
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -57,11 +58,13 @@ func testLoopOutPaymentParameters(t *testing.T) {
5758
}
5859

5960
height := int32(600)
61+
startTime := time.Unix(123, 0)
6062

6163
cfg := &swapConfig{
6264
lnd: &lnd.LndServices,
6365
store: store,
6466
server: server,
67+
clock: clock.NewTestClock(startTime),
6568
}
6669

6770
sweeper := &sweep.Sweeper{Lnd: &lnd.LndServices}
@@ -82,6 +85,7 @@ func testLoopOutPaymentParameters(t *testing.T) {
8285
)
8386
require.NoError(t, err)
8487
swap := initResult.swap
88+
require.Equal(t, startTime, swap.InitiationTime)
8589

8690
// Execute the swap in its own goroutine.
8791
errChan := make(chan error)
@@ -193,7 +197,10 @@ func testLateHtlcPublish(t *testing.T) {
193197

194198
height := int32(600)
195199

196-
cfg := newSwapConfig(&lnd.LndServices, store, server, nil)
200+
cfg := newSwapConfig(
201+
&lnd.LndServices, store, server, nil,
202+
clock.NewTestClock(time.Unix(123, 0)),
203+
)
197204

198205
testRequest.Expiry = height + testLoopOutMinOnChainCltvDelta
199206

@@ -296,6 +303,7 @@ func testCustomSweepConfTarget(t *testing.T) {
296303

297304
cfg := newSwapConfig(
298305
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
306+
clock.NewTestClock(time.Unix(123, 0)),
299307
)
300308

301309
initResult, err := newLoopOutSwap(
@@ -539,6 +547,7 @@ func testPreimagePush(t *testing.T) {
539547

540548
cfg := newSwapConfig(
541549
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
550+
clock.NewTestClock(time.Unix(123, 0)),
542551
)
543552

544553
initResult, err := newLoopOutSwap(
@@ -797,6 +806,7 @@ func testFailedOffChainCancelation(t *testing.T) {
797806

798807
cfg := newSwapConfig(
799808
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
809+
clock.NewTestClock(time.Unix(123, 0)),
800810
)
801811

802812
initResult, err := newLoopOutSwap(
@@ -951,6 +961,7 @@ func TestLoopOutMuSig2Sweep(t *testing.T) {
951961

952962
cfg := newSwapConfig(
953963
&lnd.LndServices, loopdb.NewStoreMock(t), server, nil,
964+
clock.NewTestClock(time.Unix(123, 0)),
954965
)
955966

956967
initResult, err := newLoopOutSwap(

swap.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/lightninglabs/loop/loopdb"
1010
"github.com/lightninglabs/loop/swap"
1111
"github.com/lightninglabs/loop/utils"
12+
"github.com/lightningnetwork/lnd/clock"
1213
"github.com/lightningnetwork/lnd/lntypes"
1314
)
1415

@@ -83,15 +84,18 @@ type swapConfig struct {
8384
store loopdb.SwapStore
8485
server swapServerClient
8586
assets *assets.TapdClient
87+
clock clock.Clock
8688
}
8789

8890
func newSwapConfig(lnd *lndclient.LndServices, store loopdb.SwapStore,
89-
server swapServerClient, assets *assets.TapdClient) *swapConfig {
91+
server swapServerClient, assets *assets.TapdClient,
92+
clock clock.Clock) *swapConfig {
9093

9194
return &swapConfig{
9295
lnd: lnd,
9396
store: store,
9497
server: server,
9598
assets: assets,
99+
clock: clock,
96100
}
97101
}

0 commit comments

Comments
 (0)