Skip to content

Commit 798d86a

Browse files
authored
Merge pull request #1091 from hieblmi/fix-clock
Inject clocks into swap lifecycle and update swap tests
2 parents 8640291 + b702aeb commit 798d86a

14 files changed

Lines changed: 459 additions & 121 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(

staticaddr/deposit/manager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const (
2020
// MinConfs is the minimum number of confirmations we require for a
2121
// deposit to be considered available for loop-ins, coop-spends and
2222
// timeouts.
23-
MinConfs = 6
23+
MinConfs = 3
2424

2525
// MaxConfs is unset since we don't require a max number of
2626
// confirmations for deposits.

staticaddr/loopin/actions.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/lightningnetwork/lnd/lnwallet"
3131
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
3232
"github.com/lightningnetwork/lnd/lnwire"
33+
"google.golang.org/grpc/status"
3334
)
3435

3536
const (
@@ -146,6 +147,10 @@ func (f *FSM) InitHtlcAction(ctx context.Context,
146147
ctx, loopInReq,
147148
)
148149
if err != nil {
150+
// Check if this is an insufficient confirmations error and log
151+
// the details to help the user understand what's needed.
152+
logInsufficientConfirmationsDetails(err)
153+
149154
err = fmt.Errorf("unable to initiate the loop-in with the "+
150155
"server: %w", err)
151156

@@ -914,3 +919,30 @@ func byteSliceTo66ByteSlice(b []byte) ([musig2.PubNonceSize]byte, error) {
914919

915920
return res, nil
916921
}
922+
923+
// logInsufficientConfirmationsDetails extracts and logs the per-deposit
924+
// confirmation details from a gRPC error if present.
925+
func logInsufficientConfirmationsDetails(err error) {
926+
st, ok := status.FromError(err)
927+
if !ok {
928+
return
929+
}
930+
931+
for _, detail := range st.Details() {
932+
confDetails, ok :=
933+
detail.(*swapserverrpc.InsufficientConfirmationsDetails)
934+
935+
if !ok {
936+
continue
937+
}
938+
939+
log.Warnf("Insufficient deposit confirmations, max wait: %d blocks",
940+
confDetails.MaxBlocksToWait)
941+
942+
for _, dep := range confDetails.Deposits {
943+
log.Warnf(" Deposit %s: %d/%d confirmations (need %d more blocks)",
944+
dep.Outpoint, dep.CurrentConfirmations,
945+
dep.RequiredConfirmations, dep.BlocksToWait)
946+
}
947+
}
948+
}

0 commit comments

Comments
 (0)