@@ -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
@@ -1171,9 +1174,9 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
11711174 feeRate chainfee.SatPerKWeight ) (* sweepTxCtx , error ) {
11721175
11731176 // Validate and calculate the fee and change amount.
1174- txFee , changeAmtOpt , locktimeOpt , err := prepareSweepTx (
1175- inputs , changePkScript . DeliveryAddress , feeRate ,
1176- t .currentHeight . Load () ,
1177+ txFee , changeOutputsOpt , locktimeOpt , err := prepareSweepTx (
1178+ inputs , changePkScript , feeRate , t . currentHeight . Load () ,
1179+ t .cfg . AuxSweeper ,
11771180 )
11781181 if err != nil {
11791182 return nil , err
@@ -1219,12 +1222,12 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
12191222 })
12201223 }
12211224
1222- // If there's a change amount, add it to the transaction.
1223- changeAmtOpt . WhenSome ( func ( changeAmt btcutil. Amount ) {
1224- sweepTx . AddTxOut ( & wire. TxOut {
1225- PkScript : changePkScript . DeliveryAddress ,
1226- Value : int64 ( changeAmt ),
1227- })
1225+ // If we have change outputs to add, then add it the sweep transaction
1226+ // here.
1227+ changeOutputsOpt . WhenSome ( func ( changeOuts [] SweepOutput ) {
1228+ for i := range changeOuts {
1229+ sweepTx . AddTxOut ( & changeOuts [ i ]. TxOut )
1230+ }
12281231 })
12291232
12301233 // We'll default to using the current block height as locktime, if none
@@ -1268,31 +1271,67 @@ func (t *TxPublisher) createSweepTx(inputs []input.Input,
12681271 log .Debugf ("Created sweep tx %v for inputs:\n %v" , sweepTx .TxHash (),
12691272 inputTypeSummary (inputs ))
12701273
1274+ // Try to locate the extra change output, though there might be None.
1275+ extraTxOut := fn .MapOption (
1276+ func (sweepOuts []SweepOutput ) fn.Option [SweepOutput ] {
1277+ for _ , sweepOut := range sweepOuts {
1278+ if sweepOut .IsExtra {
1279+ log .Infof ("Sweep produced " +
1280+ "extra_sweep_out=%v" ,
1281+ spew .Sdump (sweepOut ))
1282+
1283+ return fn .Some (sweepOut )
1284+ }
1285+ }
1286+
1287+ return fn .None [SweepOutput ]()
1288+ },
1289+ )(changeOutputsOpt )
1290+
12711291 return & sweepTxCtx {
1272- tx : sweepTx ,
1273- fee : txFee ,
1292+ tx : sweepTx ,
1293+ fee : txFee ,
1294+ extraTxOut : fn .FlattenOption (extraTxOut ),
12741295 }, nil
12751296}
12761297
1277- // prepareSweepTx returns the tx fee, an optional change amount and an optional
1278- // locktime after a series of validations:
1298+ // prepareSweepTx returns the tx fee, a set of optional change outputs and an
1299+ // optional locktime after a series of validations:
12791300// 1. check the locktime has been reached.
12801301// 2. check the locktimes are the same.
12811302// 3. check the inputs cover the outputs.
12821303//
12831304// NOTE: if the change amount is below dust, it will be added to the tx fee.
1284- func prepareSweepTx (inputs []input.Input , changePkScript []byte ,
1285- feeRate chainfee.SatPerKWeight , currentHeight int32 ) (
1286- btcutil.Amount , fn.Option [btcutil.Amount ], fn.Option [int32 ], error ) {
1305+ func prepareSweepTx (inputs []input.Input , changePkScript lnwallet.AddrWithKey ,
1306+ feeRate chainfee.SatPerKWeight , currentHeight int32 ,
1307+ auxSweeper fn.Option [AuxSweeper ]) (
1308+ btcutil.Amount , fn.Option [[]SweepOutput ], fn.Option [int32 ], error ) {
12871309
1288- noChange := fn .None [btcutil. Amount ]()
1310+ noChange := fn .None [[] SweepOutput ]()
12891311 noLocktime := fn .None [int32 ]()
12901312
1313+ // Given the set of inputs we have, if we have an aux sweeper, then
1314+ // we'll attempt to see if we have any other change outputs we'll need
1315+ // to add to the sweep transaction.
1316+ changePkScripts := [][]byte {changePkScript .DeliveryAddress }
1317+ extraChangeOut := fn .MapOptionZ (
1318+ auxSweeper , func (aux AuxSweeper ) fn.Result [SweepOutput ] {
1319+ return aux .DeriveSweepAddr (inputs , changePkScript )
1320+ },
1321+ )
1322+ if err := extraChangeOut .Err (); err != nil {
1323+ return 0 , noChange , noLocktime , err
1324+ }
1325+
1326+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1327+ changePkScripts = append (changePkScripts , o .PkScript )
1328+ })
1329+
12911330 // Creating a weight estimator with nil outputs and zero max fee rate.
12921331 // We don't allow adding customized outputs in the sweeping tx, and the
12931332 // fee rate is already being managed before we get here.
12941333 inputs , estimator , err := getWeightEstimate (
1295- inputs , nil , feeRate , 0 , changePkScript ,
1334+ inputs , nil , feeRate , 0 , changePkScripts ,
12961335 )
12971336 if err != nil {
12981337 return 0 , noChange , noLocktime , err
@@ -1310,6 +1349,12 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13101349 requiredOutput btcutil.Amount
13111350 )
13121351
1352+ // If we have an extra change output, then we'll add it as a required
1353+ // output amt.
1354+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1355+ requiredOutput += btcutil .Amount (o .Value )
1356+ })
1357+
13131358 // Go through each input and check if the required lock times have
13141359 // reached and are the same.
13151360 for _ , o := range inputs {
@@ -1356,14 +1401,21 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13561401 // The value remaining after the required output and fees is the
13571402 // change output.
13581403 changeAmt := totalInput - requiredOutput - txFee
1359- changeAmtOpt := fn .Some (changeAmt )
1404+ changeOuts := make ([]SweepOutput , 0 , 2 )
1405+
1406+ extraChangeOut .WhenResult (func (o SweepOutput ) {
1407+ changeOuts = append (changeOuts , o )
1408+ })
13601409
13611410 // We'll calculate the dust limit for the given changePkScript since it
13621411 // is variable.
1363- changeFloor := lnwallet .DustLimitForSize (len (changePkScript ))
1412+ changeFloor := lnwallet .DustLimitForSize (
1413+ len (changePkScript .DeliveryAddress ),
1414+ )
13641415
1365- // If the change amount is dust, we'll move it into the fees.
13661416 switch {
1417+ // If the change amount is dust, we'll move it into the fees, and
1418+ // ignore it.
13671419 case changeAmt < changeFloor :
13681420 log .Infof ("Change amt %v below dustlimit %v, not adding " +
13691421 "change output" , changeAmt , changeFloor )
@@ -1379,12 +1431,16 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13791431 // The dust amount is added to the fee.
13801432 txFee += changeAmt
13811433
1382- // Set the change amount to none.
1383- changeAmtOpt = fn .None [btcutil.Amount ]()
1384-
13851434 // Otherwise, we'll actually recognize it as a change output.
13861435 default :
1387- // TODO(roasbeef): Implement (later commit in this PR).
1436+ changeOuts = append (changeOuts , SweepOutput {
1437+ TxOut : wire.TxOut {
1438+ Value : int64 (changeAmt ),
1439+ PkScript : changePkScript .DeliveryAddress ,
1440+ },
1441+ IsExtra : false ,
1442+ InternalKey : changePkScript .InternalKey ,
1443+ })
13881444 }
13891445
13901446 // Optionally set the locktime.
@@ -1393,12 +1449,17 @@ func prepareSweepTx(inputs []input.Input, changePkScript []byte,
13931449 locktimeOpt = noLocktime
13941450 }
13951451
1452+ var changeOutsOpt fn.Option [[]SweepOutput ]
1453+ if len (changeOuts ) > 0 {
1454+ changeOutsOpt = fn .Some (changeOuts )
1455+ }
1456+
13961457 log .Debugf ("Creating sweep tx for %v inputs (%s) using %v, " +
13971458 "tx_weight=%v, tx_fee=%v, locktime=%v, parents_count=%v, " +
13981459 "parents_fee=%v, parents_weight=%v, current_height=%v" ,
13991460 len (inputs ), inputTypeSummary (inputs ), feeRate ,
14001461 estimator .weight (), txFee , locktimeOpt , len (estimator .parents ),
14011462 estimator .parentsFee , estimator .parentsWeight , currentHeight )
14021463
1403- return txFee , changeAmtOpt , locktimeOpt , nil
1464+ return txFee , changeOutsOpt , locktimeOpt , nil
14041465}
0 commit comments