@@ -8,15 +8,21 @@ import (
88
99 "github.com/btcsuite/btcd/btcutil"
1010 "github.com/btcsuite/btcd/chaincfg"
11+ "github.com/btcsuite/btcd/chaincfg/chainhash"
12+ "github.com/btcsuite/btcd/wire"
1113 "github.com/btcsuite/btclog/v2"
1214 "github.com/lightninglabs/lndclient"
1315 "github.com/lightninglabs/loop"
16+ "github.com/lightninglabs/loop/fsm"
1417 "github.com/lightninglabs/loop/labels"
1518 "github.com/lightninglabs/loop/loopdb"
1619 "github.com/lightninglabs/loop/looprpc"
20+ "github.com/lightninglabs/loop/staticaddr/address"
21+ "github.com/lightninglabs/loop/staticaddr/deposit"
1722 "github.com/lightninglabs/loop/swap"
1823 mock_lnd "github.com/lightninglabs/loop/test"
1924 "github.com/lightningnetwork/lnd/lntypes"
25+ "github.com/lightningnetwork/lnd/lnwallet"
2026 "github.com/lightningnetwork/lnd/lnwire"
2127 "github.com/lightningnetwork/lnd/routing/route"
2228 "github.com/stretchr/testify/require"
@@ -891,3 +897,233 @@ func TestListSwapsFilterAndPagination(t *testing.T) {
891897 })
892898 }
893899}
900+
901+ // mockAddressStore is a minimal in-memory store for address parameters.
902+ type mockAddressStore struct {
903+ params []* address.Parameters
904+ }
905+
906+ func (s * mockAddressStore ) CreateStaticAddress (_ context.Context ,
907+ p * address.Parameters ) error {
908+
909+ s .params = append (s .params , p )
910+ return nil
911+ }
912+
913+ func (s * mockAddressStore ) GetStaticAddress (_ context.Context , _ []byte ) (
914+ * address.Parameters , error ) {
915+
916+ if len (s .params ) == 0 {
917+ return nil , nil
918+ }
919+
920+ return s .params [0 ], nil
921+ }
922+
923+ func (s * mockAddressStore ) GetAllStaticAddresses (_ context.Context ) (
924+ []* address.Parameters , error ) {
925+
926+ return s .params , nil
927+ }
928+
929+ // mockDepositStore implements deposit.Store minimally for DepositsForOutpoints.
930+ type mockDepositStore struct {
931+ byOutpoint map [string ]* deposit.Deposit
932+ }
933+
934+ func (s * mockDepositStore ) CreateDeposit (_ context.Context ,
935+ _ * deposit.Deposit ) error {
936+
937+ return nil
938+ }
939+
940+ func (s * mockDepositStore ) UpdateDeposit (_ context.Context ,
941+ _ * deposit.Deposit ) error {
942+
943+ return nil
944+ }
945+
946+ func (s * mockDepositStore ) GetDeposit (_ context.Context ,
947+ _ deposit.ID ) (* deposit.Deposit , error ) {
948+
949+ return nil , nil
950+ }
951+
952+ func (s * mockDepositStore ) DepositForOutpoint (_ context.Context ,
953+ outpoint string ) (* deposit.Deposit , error ) {
954+
955+ if d , ok := s .byOutpoint [outpoint ]; ok {
956+ return d , nil
957+ }
958+ return nil , nil
959+ }
960+ func (s * mockDepositStore ) AllDeposits (_ context.Context ) ([]* deposit.Deposit ,
961+ error ) {
962+
963+ return nil , nil
964+ }
965+
966+ // TestListUnspentDeposits tests filtering behavior of ListUnspentDeposits.
967+ func TestListUnspentDeposits (t * testing.T ) {
968+ ctx := context .Background ()
969+ mock := mock_lnd .NewMockLnd ()
970+
971+ // Prepare a single static address parameter set.
972+ _ , client := mock_lnd .CreateKey (1 )
973+ _ , server := mock_lnd .CreateKey (2 )
974+ pkScript := []byte ("pkscript" )
975+ addrParams := & address.Parameters {
976+ ClientPubkey : client ,
977+ ServerPubkey : server ,
978+ Expiry : 10 ,
979+ PkScript : pkScript ,
980+ }
981+
982+ addrStore := & mockAddressStore {params : []* address.Parameters {addrParams }}
983+
984+ // Build an address manager using our mock lnd and fake address store.
985+ addrMgr := address .NewManager (& address.ManagerConfig {
986+ Store : addrStore ,
987+ WalletKit : mock .WalletKit ,
988+ ChainParams : mock .ChainParams ,
989+ // ChainNotifier and AddressClient are not needed for this test.
990+ }, 0 )
991+
992+ // Construct several UTXOs with different confirmation counts.
993+ makeUtxo := func (idx uint32 , confs int64 ) * lnwallet.Utxo {
994+ return & lnwallet.Utxo {
995+ AddressType : lnwallet .TaprootPubkey ,
996+ Value : btcutil .Amount (250_000 + int64 (idx )),
997+ Confirmations : confs ,
998+ PkScript : pkScript ,
999+ OutPoint : wire.OutPoint {
1000+ Hash : chainhash.Hash {byte (idx + 1 )},
1001+ Index : idx ,
1002+ },
1003+ }
1004+ }
1005+
1006+ minConfs := int64 (deposit .MinConfs )
1007+ utxoBelow := makeUtxo (0 , minConfs - 1 ) // always included
1008+ utxoAt := makeUtxo (1 , minConfs ) // included only if Deposited
1009+ utxoAbove1 := makeUtxo (2 , minConfs + 1 )
1010+ utxoAbove2 := makeUtxo (3 , minConfs + 2 )
1011+
1012+ // Helper to build the deposit manager with specific states.
1013+ buildDepositMgr := func (
1014+ states map [wire.OutPoint ]fsm.StateType ) * deposit.Manager {
1015+
1016+ store := & mockDepositStore {
1017+ byOutpoint : make (map [string ]* deposit.Deposit ),
1018+ }
1019+ for op , state := range states {
1020+ d := & deposit.Deposit {OutPoint : op }
1021+ d .SetState (state )
1022+ store .byOutpoint [op .String ()] = d
1023+ }
1024+
1025+ return deposit .NewManager (& deposit.ManagerConfig {Store : store })
1026+ }
1027+
1028+ // Include below-min-conf and >=min with Deposited; exclude others.
1029+ t .Run ("below min conf always, Deposited included, others excluded" ,
1030+ func (t * testing.T ) {
1031+ mock .SetListUnspent ([]* lnwallet.Utxo {
1032+ utxoBelow , utxoAt , utxoAbove1 , utxoAbove2 ,
1033+ })
1034+
1035+ depMgr := buildDepositMgr (map [wire.OutPoint ]fsm.StateType {
1036+ utxoAt .OutPoint : deposit .Deposited ,
1037+ utxoAbove1 .OutPoint : deposit .Withdrawn ,
1038+ utxoAbove2 .OutPoint : deposit .LoopingIn ,
1039+ })
1040+
1041+ server := & swapClientServer {
1042+ staticAddressManager : addrMgr ,
1043+ depositManager : depMgr ,
1044+ }
1045+
1046+ resp , err := server .ListUnspentDeposits (
1047+ ctx , & looprpc.ListUnspentDepositsRequest {},
1048+ )
1049+ require .NoError (t , err )
1050+
1051+ // Expect utxoBelow and utxoAt only.
1052+ require .Len (t , resp .Utxos , 2 )
1053+ got := map [string ]struct {}{}
1054+ for _ , u := range resp .Utxos {
1055+ got [u .Outpoint ] = struct {}{}
1056+ // Confirm address string is non-empty and the
1057+ // same across utxos.
1058+ require .NotEmpty (t , u .StaticAddress )
1059+ }
1060+ _ , ok1 := got [utxoBelow .OutPoint .String ()]
1061+ _ , ok2 := got [utxoAt .OutPoint .String ()]
1062+ require .True (t , ok1 )
1063+ require .True (t , ok2 )
1064+ })
1065+
1066+ // Swap states, now include utxoBelow and utxoAbove1.
1067+ t .Run ("Deposited on >=min included; non-Deposited excluded" ,
1068+ func (t * testing.T ) {
1069+ mock .SetListUnspent (
1070+ []* lnwallet.Utxo {
1071+ utxoBelow , utxoAt , utxoAbove1 ,
1072+ utxoAbove2 ,
1073+ })
1074+
1075+ depMgr := buildDepositMgr (map [wire.OutPoint ]fsm.StateType {
1076+ utxoAt .OutPoint : deposit .Withdrawn ,
1077+ utxoAbove1 .OutPoint : deposit .Deposited ,
1078+ utxoAbove2 .OutPoint : deposit .Withdrawn ,
1079+ })
1080+
1081+ server := & swapClientServer {
1082+ staticAddressManager : addrMgr ,
1083+ depositManager : depMgr ,
1084+ }
1085+
1086+ resp , err := server .ListUnspentDeposits (
1087+ ctx , & looprpc.ListUnspentDepositsRequest {},
1088+ )
1089+ require .NoError (t , err )
1090+
1091+ require .Len (t , resp .Utxos , 2 )
1092+ got := map [string ]struct {}{}
1093+ for _ , u := range resp .Utxos {
1094+ got [u .Outpoint ] = struct {}{}
1095+ }
1096+ _ , ok1 := got [utxoBelow .OutPoint .String ()]
1097+ _ , ok2 := got [utxoAbove1 .OutPoint .String ()]
1098+ require .True (t , ok1 )
1099+ require .True (t , ok2 )
1100+ })
1101+
1102+ // Confirmed UTXO not present in store should be included.
1103+ t .Run ("confirmed utxo not in store is included" , func (t * testing.T ) {
1104+ // Only return a confirmed UTXO from lnd and make sure the
1105+ // deposit manager/store doesn't know about it.
1106+ mock .SetListUnspent ([]* lnwallet.Utxo {utxoAbove2 })
1107+
1108+ // Empty store (no states for any outpoint).
1109+ depMgr := buildDepositMgr (map [wire.OutPoint ]fsm.StateType {})
1110+
1111+ server := & swapClientServer {
1112+ staticAddressManager : addrMgr ,
1113+ depositManager : depMgr ,
1114+ }
1115+
1116+ resp , err := server .ListUnspentDeposits (
1117+ ctx , & looprpc.ListUnspentDepositsRequest {},
1118+ )
1119+ require .NoError (t , err )
1120+
1121+ // We expect the confirmed UTXO to be included even though it
1122+ // doesn't exist in the store yet.
1123+ require .Len (t , resp .Utxos , 1 )
1124+ require .Equal (
1125+ t , utxoAbove2 .OutPoint .String (), resp .Utxos [0 ].Outpoint ,
1126+ )
1127+ require .NotEmpty (t , resp .Utxos [0 ].StaticAddress )
1128+ })
1129+ }
0 commit comments