package loopin import ( "context" "testing" "time" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/lightninglabs/loop/loopdb" "github.com/lightninglabs/loop/staticaddr/deposit" "github.com/lightninglabs/loop/test" "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/lntypes" "github.com/stretchr/testify/require" ) // TestGetStaticAddressLoopInSwapsByStates tests that we can retrieve // StaticAddressLoopIn swaps by their states and that the deposits // associated with the swaps are correctly populated. func TestGetStaticAddressLoopInSwapsByStates(t *testing.T) { // Set up test context objects. ctxb := context.Background() testDb := loopdb.NewTestDB(t) testClock := clock.NewTestClock(time.Now()) defer testDb.Close() depositStore := deposit.NewSqlStore(testDb.BaseDB) swapStore := NewSqlStore( loopdb.NewTypedStore[Querier](testDb), testClock, &chaincfg.RegressionNetParams, ) newID := func() deposit.ID { did, err := deposit.GetRandomDepositID() require.NoError(t, err) return did } loopingDepositID := newID() loopedInDepositID := newID() d1, d2 := &deposit.Deposit{ ID: loopingDepositID, OutPoint: wire.OutPoint{ Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, Index: 0, }, Value: btcutil.Amount(100_000), TimeOutSweepPkScript: []byte{ 0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41, }, }, &deposit.Deposit{ ID: loopedInDepositID, OutPoint: wire.OutPoint{ Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, Index: 1, }, Value: btcutil.Amount(200_000), TimeOutSweepPkScript: []byte{ 0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d, }, } err := depositStore.CreateDeposit(ctxb, d1) require.NoError(t, err) err = depositStore.CreateDeposit(ctxb, d2) require.NoError(t, err) // Add two updates per deposit, expect the last to be retrieved. d1.SetState(deposit.Deposited) d2.SetState(deposit.Deposited) err = depositStore.UpdateDeposit(ctxb, d1) require.NoError(t, err) err = depositStore.UpdateDeposit(ctxb, d2) require.NoError(t, err) d1.SetState(deposit.LoopingIn) d2.SetState(deposit.LoopedIn) err = depositStore.UpdateDeposit(ctxb, d1) require.NoError(t, err) err = depositStore.UpdateDeposit(ctxb, d2) require.NoError(t, err) _, clientPubKey := test.CreateKey(1) _, serverPubKey := test.CreateKey(2) addr, err := btcutil.DecodeAddress(P2wkhAddr, nil) require.NoError(t, err) // Create pending swap. swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4} swapPending := StaticAddressLoopIn{ SwapHash: swapHashPending, SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4}, DepositOutpoints: []string{d1.OutPoint.String()}, Deposits: []*deposit.Deposit{d1}, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, } swapPending.SetState(SignHtlcTx) err = swapStore.CreateLoopIn(ctxb, &swapPending) require.NoError(t, err) // Create succeeded swap. swapHashSucceeded := lntypes.Hash{0x2, 0x2, 0x3, 0x5} swapSucceeded := StaticAddressLoopIn{ SwapHash: swapHashSucceeded, SwapPreimage: lntypes.Preimage{0x2, 0x2, 0x3, 0x5}, DepositOutpoints: []string{d2.OutPoint.String()}, Deposits: []*deposit.Deposit{d2}, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, } swapSucceeded.SetState(Succeeded) err = swapStore.CreateLoopIn(ctxb, &swapSucceeded) require.NoError(t, err) pendingSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, PendingStates) require.NoError(t, err) require.Len(t, pendingSwaps, 1) require.Equal(t, swapHashPending, pendingSwaps[0].SwapHash) require.Equal(t, []string{d1.OutPoint.String()}, pendingSwaps[0].DepositOutpoints) require.Equal(t, SignHtlcTx, pendingSwaps[0].GetState()) pendingDeposits := pendingSwaps[0].Deposits require.Len(t, pendingDeposits, 1) require.Equal(t, d1.ID, pendingDeposits[0].ID) require.Equal(t, d1.OutPoint, pendingDeposits[0].OutPoint) require.Equal(t, d1.Value, pendingDeposits[0].Value) require.Equal(t, deposit.LoopingIn, pendingDeposits[0].GetState()) finalizedSwaps, err := swapStore.GetStaticAddressLoopInSwapsByStates(ctxb, FinalStates) require.NoError(t, err) require.Len(t, finalizedSwaps, 1) require.Equal(t, swapHashSucceeded, finalizedSwaps[0].SwapHash) require.Equal(t, []string{d2.OutPoint.String()}, finalizedSwaps[0].DepositOutpoints) require.Equal(t, Succeeded, finalizedSwaps[0].GetState()) finalizedDeposits := finalizedSwaps[0].Deposits require.Len(t, finalizedDeposits, 1) require.Equal(t, d2.ID, finalizedDeposits[0].ID) require.Equal(t, d2.OutPoint, finalizedDeposits[0].OutPoint) require.Equal(t, d2.Value, finalizedDeposits[0].Value) require.Equal(t, deposit.LoopedIn, finalizedDeposits[0].GetState()) } // TestCreateLoopIn tests that CreateLoopIn correctly creates a new // StaticAddressLoopIn swap and associates it with the provided deposits. func TestCreateLoopIn(t *testing.T) { // Set up test context objects. ctx := t.Context() testDb := loopdb.NewTestDB(t) testClock := clock.NewTestClock(time.Now()) defer testDb.Close() depositStore := deposit.NewSqlStore(testDb.BaseDB) swapStore := NewSqlStore( loopdb.NewTypedStore[Querier](testDb), testClock, &chaincfg.RegressionNetParams, ) newID := func() deposit.ID { did, err := deposit.GetRandomDepositID() require.NoError(t, err) return did } d1, d2 := &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ Hash: chainhash.Hash{0x1a, 0x2b, 0x3c, 0x4d}, Index: 0, }, Value: btcutil.Amount(100_000), TimeOutSweepPkScript: []byte{ 0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41, }, }, &deposit.Deposit{ ID: newID(), OutPoint: wire.OutPoint{ Hash: chainhash.Hash{0x2a, 0x2b, 0x3c, 0x4e}, Index: 1, }, Value: btcutil.Amount(200_000), TimeOutSweepPkScript: []byte{ 0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d, }, } err := depositStore.CreateDeposit(ctx, d1) require.NoError(t, err) err = depositStore.CreateDeposit(ctx, d2) require.NoError(t, err) d1.SetState(deposit.LoopingIn) d2.SetState(deposit.LoopingIn) err = depositStore.UpdateDeposit(ctx, d1) require.NoError(t, err) err = depositStore.UpdateDeposit(ctx, d2) require.NoError(t, err) _, clientPubKey := test.CreateKey(1) _, serverPubKey := test.CreateKey(2) addr, err := btcutil.DecodeAddress(P2wkhAddr, nil) require.NoError(t, err) // Create pending swap. swapHashPending := lntypes.Hash{0x1, 0x2, 0x3, 0x4} swapPending := StaticAddressLoopIn{ SwapHash: swapHashPending, SwapPreimage: lntypes.Preimage{0x1, 0x2, 0x3, 0x4}, DepositOutpoints: []string{d1.OutPoint.String(), d2.OutPoint.String()}, Deposits: []*deposit.Deposit{d1, d2}, ClientPubkey: clientPubKey, ServerPubkey: serverPubKey, HtlcTimeoutSweepAddress: addr, } swapPending.SetState(SignHtlcTx) err = swapStore.CreateLoopIn(ctx, &swapPending) require.NoError(t, err) depositIDs, err := swapStore.DepositIDsForSwapHash( ctx, swapHashPending, ) require.NoError(t, err) require.Len(t, depositIDs, 2) require.Contains(t, depositIDs, d1.ID) require.Contains(t, depositIDs, d2.ID) swapHashes, err := swapStore.SwapHashesForDepositIDs( ctx, []deposit.ID{depositIDs[0], depositIDs[1]}, ) require.NoError(t, err) require.Len(t, swapHashes, 1) require.Len(t, swapHashes[swapHashPending], 2) require.Contains(t, swapHashes[swapHashPending], depositIDs[0]) require.Contains(t, swapHashes[swapHashPending], depositIDs[1]) swap, err := swapStore.GetLoopInByHash(ctx, swapHashPending) require.NoError(t, err) require.Equal(t, swapHashPending, swap.SwapHash) require.Equal(t, []string{d1.OutPoint.String(), d2.OutPoint.String()}, swap.DepositOutpoints) require.Equal(t, SignHtlcTx, swap.GetState()) require.Len(t, swap.Deposits, 2) require.Equal(t, d1.ID, swap.Deposits[0].ID) require.Equal(t, d1.OutPoint, swap.Deposits[0].OutPoint) require.Equal(t, d1.Value, swap.Deposits[0].Value) require.Equal(t, deposit.LoopingIn, swap.Deposits[0].GetState()) require.Equal(t, d2.ID, swap.Deposits[1].ID) require.Equal(t, d2.OutPoint, swap.Deposits[1].OutPoint) require.Equal(t, d2.Value, swap.Deposits[1].Value) require.Equal(t, deposit.LoopingIn, swap.Deposits[1].GetState()) updateTime := testClock.Now().Add(time.Minute) testClock.SetTime(updateTime) swapPending.SetState(Succeeded) err = swapStore.UpdateLoopIn(ctx, &swapPending) require.NoError(t, err) 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, ) }