@@ -172,6 +172,10 @@ type AnchorVTxnsParams struct {
172172 // PassivePackets is a list of all the virtual transactions which
173173 // re-anchor passive assets.
174174 PassivePackets []* tappsbt.VPacket
175+
176+ // ZeroValueInputs is a list of zero-value UTXOs that should be swept
177+ // as additional inputs to the transaction.
178+ ZeroValueInputs []ZeroValueInput
175179}
176180
177181// WalletConfig holds the configuration for a new Wallet.
@@ -248,6 +252,10 @@ type FundedVPacket struct {
248252 // InputCommitments is a map from virtual package input index to its
249253 // associated Taproot Asset commitment.
250254 InputCommitments tappsbt.InputCommitments
255+
256+ // ZeroValueInputs is a list of zero-value UTXOs that should be swept
257+ // as additional inputs to the transaction.
258+ ZeroValueInputs []ZeroValueInput
251259}
252260
253261// FundAddressSend funds a virtual transaction, selecting assets to spend in
@@ -684,6 +692,17 @@ func (f *AssetWallet) FundPacket(ctx context.Context,
684692 return nil , err
685693 }
686694
695+ // Fetch zero-value UTXOs that should be swept as additional inputs.
696+ zeroValueInputs , err := f .cfg .CoinSelector .FetchZeroValueAnchorUTXOs (
697+ ctx ,
698+ )
699+ if err != nil {
700+ return nil , fmt .Errorf ("unable to fetch zero-value " +
701+ "UTXOs: %w" , err )
702+ }
703+
704+ pkt .ZeroValueInputs = zeroValueInputs
705+
687706 success = true
688707 return pkt , nil
689708}
@@ -810,6 +829,17 @@ func (f *AssetWallet) FundBurn(ctx context.Context,
810829 len (fundedPkt .VPackets ))
811830 }
812831
832+ // Fetch zero-value UTXOs that should be swept as additional inputs.
833+ zeroValueInputs , err := f .cfg .CoinSelector .FetchZeroValueAnchorUTXOs (
834+ ctx ,
835+ )
836+ if err != nil {
837+ return nil , fmt .Errorf ("unable to fetch zero-value " +
838+ "UTXOs: %w" , err )
839+ }
840+
841+ fundedPkt .ZeroValueInputs = zeroValueInputs
842+
813843 // Don't release the coins we've selected, as so far we've been
814844 // successful.
815845 success = true
@@ -1191,28 +1221,33 @@ func (f *AssetWallet) AnchorVirtualTransactions(ctx context.Context,
11911221 // it itself.
11921222 addAnchorPsbtInputs (sendPacket , params .ActivePackets )
11931223
1224+ // Add zero-value inputs that should be swept as additional inputs.
1225+ numZeroValueInputs := len (params .ZeroValueInputs )
1226+ if numZeroValueInputs > 0 {
1227+ log .Infof ("Sweeping %d zero-value UTXOs" , numZeroValueInputs )
1228+ addZeroValuePsbtInputs (
1229+ sendPacket , params .ZeroValueInputs ,
1230+ f .cfg .ChainParams .HDCoinType ,
1231+ )
1232+ }
1233+
11941234 // We now fund the packet, placing the change on the last output.
11951235 anchorPkt , err := f .cfg .Wallet .FundPsbt (
11961236 ctx , sendPacket , 1 , params .FeeRate , - 1 ,
11971237 )
11981238 if err != nil {
11991239 return nil , fmt .Errorf ("unable to fund psbt: %w" , err )
12001240 }
1201-
1202- log .Infof ("Received funded PSBT packet" )
1203- log .Tracef ("Packet: %v" , spew .Sdump (anchorPkt .Pkt ))
1241+ log .Tracef ("Got funded PSBT packet: %v" , spew .Sdump (anchorPkt .Pkt ))
12041242
12051243 // With all the input and output information in the packet, we
12061244 // can now ask lnd to sign it, and then extract the final
12071245 // version ourselves.
1208- log .Debugf ("Signing PSBT" )
1209- log .Tracef ("PSBT: %s" , spew .Sdump (anchorPkt ))
12101246 signedPsbt , err := f .cfg .Wallet .SignPsbt (ctx , anchorPkt .Pkt )
12111247 if err != nil {
12121248 return nil , fmt .Errorf ("unable to sign psbt: %w" , err )
12131249 }
1214- log .Debugf ("Got signed PSBT" )
1215- log .Tracef ("PSBT: %s" , spew .Sdump (signedPsbt ))
1250+ log .Tracef ("Got signed PSBT: %s" , spew .Sdump (signedPsbt ))
12161251
12171252 // Before we finalize, we need to calculate the actual, final fees that
12181253 // we pay.
@@ -1380,3 +1415,44 @@ func addAnchorPsbtInputs(btcPkt *psbt.Packet, vPackets []*tappsbt.VPacket) {
13801415
13811416 }
13821417}
1418+
1419+ // addZeroValuePsbtInputs adds zero-value UTXOs as inputs to the PSBT.
1420+ func addZeroValuePsbtInputs (btcPkt * psbt.Packet ,
1421+ zeroValueInputs []ZeroValueInput , coinType uint32 ) {
1422+
1423+ for _ , utxo := range zeroValueInputs {
1424+ // Check if this input is already added to avoid duplicates.
1425+ if tapsend .HasInput (btcPkt .UnsignedTx , utxo .GetOutPoint ()) {
1426+ continue
1427+ }
1428+
1429+ // Create the BIP32 derivation info for signing.
1430+ bip32Derivation , trDerivation := tappsbt .
1431+ Bip32DerivationFromKeyDesc (
1432+ utxo .GetInternalKey (), coinType ,
1433+ )
1434+
1435+ btcPkt .Inputs = append (btcPkt .Inputs , psbt.PInput {
1436+ WitnessUtxo : & wire.TxOut {
1437+ Value : int64 (utxo .GetOutputValue ()),
1438+ PkScript : utxo .GetPkScript (),
1439+ },
1440+ SighashType : txscript .SigHashDefault ,
1441+ Bip32Derivation : []* psbt.Bip32Derivation {
1442+ bip32Derivation ,
1443+ },
1444+ TaprootInternalKey : schnorr .SerializePubKey (
1445+ utxo .GetInternalKey ().PubKey ,
1446+ ),
1447+ TaprootBip32Derivation : []* psbt.TaprootBip32Derivation {
1448+ trDerivation ,
1449+ },
1450+ TaprootMerkleRoot : utxo .GetMerkleRoot (),
1451+ })
1452+ btcPkt .UnsignedTx .TxIn = append (
1453+ btcPkt .UnsignedTx .TxIn , & wire.TxIn {
1454+ PreviousOutPoint : utxo .GetOutPoint (),
1455+ },
1456+ )
1457+ }
1458+ }
0 commit comments