loop/staticaddr/withdraw/sql_store_test.go
Boris Nagaev 8ac8e3801c
staticaddr: model pending withdrawals
Represent withdrawal txids as optional in the domain model and keep
pending withdrawals visible through GetAllWithdrawals.

Pending rows now surface nil and zero values in Go and empty and zero
defaults over RPC, while malformed non-NULL txids still fail loudly.

Also sync godoc's with how it actually behaves: returns pending withdraws
in addition to finalized ones.
2026-04-07 01:47:31 -05:00

154 lines
3.9 KiB
Go

package withdraw
import (
"context"
"database/sql"
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightninglabs/loop/loopdb/sqlc"
"github.com/lightninglabs/loop/staticaddr/deposit"
"github.com/stretchr/testify/require"
)
// TestSqlStore tests the basic functionality of the SQLStore.
func TestSqlStore(t *testing.T) {
ctxb := context.Background()
testDb := loopdb.NewTestDB(t)
defer testDb.Close()
depositStore := deposit.NewSqlStore(testDb.BaseDB)
store := NewSqlStore(loopdb.NewTypedStore[Querier](testDb), depositStore)
newID := func() deposit.ID {
did, err := deposit.GetRandomDepositID()
require.NoError(t, err)
return did
}
d1, d2 := &deposit.Deposit{
ID: newID(),
Value: btcutil.Amount(100_000),
TimeOutSweepPkScript: []byte{
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41,
},
},
&deposit.Deposit{
ID: newID(),
Value: btcutil.Amount(200_000),
TimeOutSweepPkScript: []byte{
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x4d,
},
}
withdrawalTx := &wire.MsgTx{
Version: 2,
TxOut: []*wire.TxOut{
{
Value: int64(d1.Value + d2.Value - 100),
PkScript: []byte{
0x00,
},
},
{
Value: int64(100),
PkScript: []byte{
0x01,
},
},
},
}
err := depositStore.CreateDeposit(ctxb, d1)
require.NoError(t, err)
err = depositStore.CreateDeposit(ctxb, d2)
require.NoError(t, err)
err = store.CreateWithdrawal(ctxb, []*deposit.Deposit{d1, d2})
require.NoError(t, err)
withdrawals, err := store.GetAllWithdrawals(ctxb)
require.NoError(t, err)
require.Len(t, withdrawals, 1)
require.NotEmpty(t, withdrawals[0].ID)
require.EqualValues(
t, d1.Value+d2.Value, withdrawals[0].TotalDepositAmount,
)
require.Len(t, withdrawals[0].Deposits, 2)
require.EqualValues(
t, d1.Value, withdrawals[0].Deposits[0].Value,
)
require.EqualValues(
t, d2.Value, withdrawals[0].Deposits[1].Value,
)
require.NotEmpty(t, withdrawals[0].InitiationTime)
require.Nil(t, withdrawals[0].TxID)
require.Zero(t, withdrawals[0].WithdrawnAmount)
require.Zero(t, withdrawals[0].ChangeAmount)
require.Zero(t, withdrawals[0].ConfirmationHeight)
err = store.UpdateWithdrawal(
ctxb, []*deposit.Deposit{d1, d2}, withdrawalTx, 6, []byte{0x01},
)
require.NoError(t, err)
withdrawals, err = store.GetAllWithdrawals(ctxb)
require.NoError(t, err)
require.Len(t, withdrawals, 1)
require.NotNil(t, withdrawals[0].TxID)
require.Equal(t, withdrawalTx.TxHash(), *withdrawals[0].TxID)
require.EqualValues(
t, d1.Value+d2.Value-100, withdrawals[0].WithdrawnAmount,
)
require.EqualValues(t, 100, withdrawals[0].ChangeAmount)
require.EqualValues(t, 6, withdrawals[0].ConfirmationHeight)
}
// TestGetAllWithdrawalsRejectsInvalidTxID verifies that a malformed persisted
// withdrawal txid is rejected, while pending withdrawals remain readable via
// NULL values.
func TestGetAllWithdrawalsRejectsInvalidTxID(t *testing.T) {
ctxb := context.Background()
testDb := loopdb.NewTestDB(t)
defer testDb.Close()
depositStore := deposit.NewSqlStore(testDb.BaseDB)
store := NewSqlStore(loopdb.NewTypedStore[Querier](testDb), depositStore)
depositID, err := deposit.GetRandomDepositID()
require.NoError(t, err)
d := &deposit.Deposit{
ID: depositID,
Value: btcutil.Amount(100_000),
TimeOutSweepPkScript: []byte{
0x00, 0x14, 0x1a, 0x2b, 0x3c, 0x41,
},
}
err = depositStore.CreateDeposit(ctxb, d)
require.NoError(t, err)
err = store.CreateWithdrawal(ctxb, []*deposit.Deposit{d})
require.NoError(t, err)
withdrawalID, err := testDb.Queries.GetWithdrawalIDByDepositID(
ctxb, d.ID[:],
)
require.NoError(t, err)
err = testDb.Queries.UpdateWithdrawal(ctxb, sqlc.UpdateWithdrawalParams{
WithdrawalID: withdrawalID,
WithdrawalTxID: sql.NullString{
String: "abcd",
Valid: true,
},
})
require.NoError(t, err)
_, err = store.GetAllWithdrawals(ctxb)
require.ErrorContains(t, err, "invalid withdrawal txid")
}