@@ -12,6 +12,7 @@ import (
1212 "github.com/btcsuite/btcd/txscript"
1313 "github.com/btcsuite/btcd/wire"
1414 "github.com/btcsuite/btcwallet/chain"
15+ "github.com/davecgh/go-spew/spew"
1516 "github.com/lightningnetwork/lnd/chainntnfs"
1617 "github.com/lightningnetwork/lnd/fn"
1718 "github.com/lightningnetwork/lnd/input"
@@ -131,6 +132,8 @@ type BumpRequest struct {
131132func (r * BumpRequest ) MaxFeeRateAllowed () (chainfee.SatPerKWeight , error ) {
132133 // Get the size of the sweep tx, which will be used to calculate the
133134 // budget fee rate.
135+ //
136+ // TODO(roasbeef): also wants the extra change output?
134137 size , err := calcSweepTxWeight (
135138 r .Inputs , r .DeliveryAddress .DeliveryAddress ,
136139 )
@@ -175,7 +178,7 @@ func calcSweepTxWeight(inputs []input.Input,
175178 // TODO(yy): we should refactor the weight estimator to not require a
176179 // fee rate and max fee rate and make it a pure tx weight calculator.
177180 _ , estimator , err := getWeightEstimate (
178- inputs , nil , feeRate , 0 , outputPkScript ,
181+ inputs , nil , feeRate , 0 , [][] byte { outputPkScript } ,
179182 )
180183 if err != nil {
181184 return 0 , err
@@ -1159,9 +1162,9 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
11591162 feeRate chainfee.SatPerKWeight ) (* sweepTxCtx , error ) {
11601163
11611164 // Validate and calculate the fee and change amount.
1162- txFee , changeAmtOpt , locktimeOpt , err := prepareSweepTx (
1163- inputs , changePkScript . DeliveryAddress , feeRate ,
1164- t .currentHeight . Load () ,
1165+ txFee , changeOutputsOpt , locktimeOpt , err := prepareSweepTx (
1166+ inputs , changePkScript , feeRate , t . currentHeight . Load () ,
1167+ t .cfg . AuxSweeper ,
11651168 )
11661169 if err != nil {
11671170 return nil , err
@@ -1207,12 +1210,12 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
12071210 })
12081211 }
12091212
1210- // If there's a change amount, add it to the transaction.
1211- changeAmtOpt . WhenSome ( func ( changeAmt btcutil. Amount ) {
1212- sweepTx . AddTxOut ( & wire. TxOut {
1213- PkScript : changePkScript . DeliveryAddress ,
1214- Value : int64 ( changeAmt ),
1215- })
1213+ // If we have change outputs to add, then add it the sweep transaction
1214+ // here.
1215+ changeOutputsOpt . WhenSome ( func ( changeOuts [] SweepOutput ) {
1216+ for i := range changeOuts {
1217+ sweepTx . AddTxOut ( & changeOuts [ i ]. TxOut )
1218+ }
12161219 })
12171220
12181221 // We'll default to using the current block height as locktime, if none
@@ -1256,31 +1259,64 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
12561259 log .Debugf ("Created sweep tx %v for inputs:\n %v" , sweepTx .TxHash (),
12571260 inputTypeSummary (inputs ))
12581261
1262+ // Try to locate the extra change output, though there might be None.
1263+ extraTxOut := fn .MapOption (func (sweepOuts []SweepOutput ) fn.Option [SweepOutput ] { //nolint:lll
1264+ for _ , sweepOut := range sweepOuts {
1265+ if sweepOut .IsExtra {
1266+ log .Infof ("Sweep produced extra_sweep_out=%v" ,
1267+ spew .Sdump (sweepOut ))
1268+
1269+ return fn .Some (sweepOut )
1270+ }
1271+ }
1272+
1273+ return fn .None [SweepOutput ]()
1274+ })(changeOutputsOpt )
1275+
12591276 return & sweepTxCtx {
1260- tx : sweepTx ,
1261- fee : txFee ,
1277+ tx : sweepTx ,
1278+ fee : txFee ,
1279+ extraTxOut : fn .FlattenOption (extraTxOut ),
12621280 }, nil
12631281}
12641282
1265- // prepareSweepTx returns the tx fee, an optional change amount and an optional
1266- // locktime after a series of validations:
1283+ // prepareSweepTx returns the tx fee, a set of optional change outputs and an
1284+ // optional locktime after a series of validations:
12671285// 1. check the locktime has been reached.
12681286// 2. check the locktimes are the same.
12691287// 3. check the inputs cover the outputs.
12701288//
12711289// NOTE: if the change amount is below dust, it will be added to the tx fee.
1272- func prepareSweepTx (inputs []input.Input , changePkScript []byte ,
1273- feeRate chainfee.SatPerKWeight , currentHeight int32 ) (
1274- btcutil.Amount , fn.Option [btcutil.Amount ], fn.Option [int32 ], error ) {
1290+ func prepareSweepTx (inputs []input.Input , changePkScript lnwallet.AddrWithKey ,
1291+ feeRate chainfee.SatPerKWeight , currentHeight int32 ,
1292+ auxSweeper fn.Option [AuxSweeper ]) (
1293+ btcutil.Amount , fn.Option [[]SweepOutput ], fn.Option [int32 ], error ) {
12751294
1276- noChange := fn .None [btcutil. Amount ]()
1295+ noChange := fn .None [[] SweepOutput ]()
12771296 noLocktime := fn .None [int32 ]()
12781297
1298+ // Given the set of inputs we have, if we have an aux sweeper, then
1299+ // we'll attempt to see if we have any other change outputs we'll need
1300+ // to add to the sweep transaction.
1301+ changePkScripts := [][]byte {changePkScript .DeliveryAddress }
1302+ extraChangeOut := fn .MapOptionZ (
1303+ auxSweeper ,
1304+ func (aux AuxSweeper ) fn.Result [SweepOutput ] {
1305+ return aux .DeriveSweepAddr (inputs , changePkScript )
1306+ },
1307+ )
1308+ if err := extraChangeOut .Err (); err != nil {
1309+ return 0 , noChange , noLocktime , err
1310+ }
1311+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1312+ changePkScripts = append (changePkScripts , o .PkScript )
1313+ })
1314+
12791315 // Creating a weight estimator with nil outputs and zero max fee rate.
12801316 // We don't allow adding customized outputs in the sweeping tx, and the
12811317 // fee rate is already being managed before we get here.
12821318 inputs , estimator , err := getWeightEstimate (
1283- inputs , nil , feeRate , 0 , changePkScript ,
1319+ inputs , nil , feeRate , 0 , changePkScripts ,
12841320 )
12851321 if err != nil {
12861322 return 0 , noChange , noLocktime , err
@@ -1298,6 +1334,12 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
12981334 requiredOutput btcutil.Amount
12991335 )
13001336
1337+ // If we have an extra change output, then we'll add it as a required
1338+ // output amt.
1339+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1340+ requiredOutput += btcutil .Amount (o .Value )
1341+ })
1342+
13011343 // Go through each input and check if the required lock times have
13021344 // reached and are the same.
13031345 for _ , o := range inputs {
@@ -1345,14 +1387,21 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13451387 // change output.
13461388 changeAmt := totalInput - requiredOutput - txFee
13471389
1390+ changeOuts := make ([]SweepOutput , 0 , 2 )
1391+
1392+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1393+ changeOuts = append (changeOuts , o )
1394+ })
1395+
13481396 // We'll calculate the dust limit for the given changePkScript since it
13491397 // is variable.
1350- changeFloor := lnwallet .DustLimitForSize (len ( changePkScript ))
1351-
1352- changeAmtOpt := fn . Some ( changeAmt )
1398+ changeFloor := lnwallet .DustLimitForSize (
1399+ len ( changePkScript . DeliveryAddress ),
1400+ )
13531401
1354- // If the change amount is dust, we'll move it into the fees.
13551402 switch {
1403+ // If the change amount is dust, we'll move it into the fees, and
1404+ // ignore it.
13561405 case changeAmt < changeFloor :
13571406 log .Infof ("Change amt %v below dustlimit %v, not adding " +
13581407 "change output" , changeAmt , changeFloor )
@@ -1368,8 +1417,16 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13681417 // The dust amount is added to the fee.
13691418 txFee += changeAmt
13701419
1371- // Set the change amount to none.
1372- changeAmtOpt = fn .None [btcutil.Amount ]()
1420+ // Otherwise, we'll actually recognize it as a change output.
1421+ default :
1422+ changeOuts = append (changeOuts , SweepOutput {
1423+ TxOut : wire.TxOut {
1424+ Value : int64 (changeAmt ),
1425+ PkScript : changePkScript .DeliveryAddress ,
1426+ },
1427+ IsExtra : false ,
1428+ InternalKey : changePkScript .InternalKey ,
1429+ })
13731430 }
13741431
13751432 // Optionally set the locktime.
@@ -1378,12 +1435,17 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13781435 locktimeOpt = noLocktime
13791436 }
13801437
1438+ var changeOutsOpt fn.Option [[]SweepOutput ]
1439+ if len (changeOuts ) > 0 {
1440+ changeOutsOpt = fn .Some (changeOuts )
1441+ }
1442+
13811443 log .Debugf ("Creating sweep tx for %v inputs (%s) using %v, " +
13821444 "tx_weight=%v, tx_fee=%v, locktime=%v, parents_count=%v, " +
13831445 "parents_fee=%v, parents_weight=%v, current_height=%v" ,
13841446 len (inputs ), inputTypeSummary (inputs ), feeRate ,
13851447 estimator .weight (), txFee , locktimeOpt , len (estimator .parents ),
13861448 estimator .parentsFee , estimator .parentsWeight , currentHeight )
13871449
1388- return txFee , changeAmtOpt , locktimeOpt , nil
1450+ return txFee , changeOutsOpt , locktimeOpt , nil
13891451}
0 commit comments