@@ -23,6 +23,7 @@ import (
2323 "github.com/lightningnetwork/lnd/lnutils"
2424 "github.com/lightningnetwork/lnd/lnwallet"
2525 "github.com/lightningnetwork/lnd/lnwallet/chainfee"
26+ "github.com/lightningnetwork/lnd/sweep"
2627 "github.com/lightningnetwork/lnd/tlv"
2728)
2829
@@ -174,6 +175,10 @@ type BreachConfig struct {
174175 // breached channels. This is used in conjunction with DB to recover
175176 // from crashes, restarts, or other failures.
176177 Store RetributionStorer
178+
179+ // AuxSweeper is an optional interface that can be used to modify the
180+ // way sweep transaction are generated.
181+ AuxSweeper fn.Option [sweep.AuxSweeper ]
177182}
178183
179184// BreachArbitrator is a special subsystem which is responsible for watching and
@@ -737,10 +742,28 @@ justiceTxBroadcast:
737742 brarLog .Debugf ("Broadcasting justice tx: %v" , lnutils .SpewLogClosure (
738743 finalTx ))
739744
745+ // As we're about to broadcast our breach transaction, we'll notify the
746+ // aux sweeper of our broadcast attempt first.
747+ err = fn .MapOptionZ (b .cfg .AuxSweeper , func (aux sweep.AuxSweeper ) error {
748+ bumpReq := sweep.BumpRequest {
749+ Inputs : finalTx .inputs ,
750+ DeliveryAddress : finalTx .sweepAddr ,
751+ ExtraTxOut : finalTx .extraTxOut ,
752+ }
753+
754+ return aux .NotifyBroadcast (
755+ & bumpReq , finalTx .justiceTx , finalTx .fee ,
756+ )
757+ })
758+ if err != nil {
759+ brarLog .Errorf ("unable to notify broadcast: %w" , err )
760+ return
761+ }
762+
740763 // We'll now attempt to broadcast the transaction which finalized the
741764 // channel's retribution against the cheating counter party.
742765 label := labels .MakeLabel (labels .LabelTypeJusticeTransaction , nil )
743- err = b .cfg .PublishTransaction (finalTx , label )
766+ err = b .cfg .PublishTransaction (finalTx . justiceTx , label )
744767 if err != nil {
745768 brarLog .Errorf ("Unable to broadcast justice tx: %v" , err )
746769 }
@@ -860,7 +883,9 @@ Loop:
860883 "spending commitment outs: %v" ,
861884 lnutils .SpewLogClosure (tx ))
862885
863- err = b .cfg .PublishTransaction (tx , label )
886+ err = b .cfg .PublishTransaction (
887+ tx .justiceTx , label ,
888+ )
864889 if err != nil {
865890 brarLog .Warnf ("Unable to broadcast " +
866891 "commit out spending justice " +
@@ -875,7 +900,9 @@ Loop:
875900 "spending HTLC outs: %v" ,
876901 lnutils .SpewLogClosure (tx ))
877902
878- err = b .cfg .PublishTransaction (tx , label )
903+ err = b .cfg .PublishTransaction (
904+ tx .justiceTx , label ,
905+ )
879906 if err != nil {
880907 brarLog .Warnf ("Unable to broadcast " +
881908 "HTLC out spending justice " +
@@ -890,7 +917,9 @@ Loop:
890917 "spending second-level HTLC output: %v" ,
891918 lnutils .SpewLogClosure (tx ))
892919
893- err = b .cfg .PublishTransaction (tx , label )
920+ err = b .cfg .PublishTransaction (
921+ tx .justiceTx , label ,
922+ )
894923 if err != nil {
895924 brarLog .Warnf ("Unable to broadcast " +
896925 "second-level HTLC out " +
@@ -1372,10 +1401,10 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
13721401// spend the to_local output and commitment level HTLC outputs separately,
13731402// before the CSV locks expire.
13741403type justiceTxVariants struct {
1375- spendAll * wire. MsgTx
1376- spendCommitOuts * wire. MsgTx
1377- spendHTLCs * wire. MsgTx
1378- spendSecondLevelHTLCs []* wire. MsgTx
1404+ spendAll * justiceTxCtx
1405+ spendCommitOuts * justiceTxCtx
1406+ spendHTLCs * justiceTxCtx
1407+ spendSecondLevelHTLCs []* justiceTxCtx
13791408}
13801409
13811410// createJusticeTx creates transactions which exacts "justice" by sweeping ALL
@@ -1439,7 +1468,9 @@ func (b *BreachArbitrator) createJusticeTx(
14391468 err )
14401469 }
14411470
1442- secondLevelSweeps := make ([]* wire.MsgTx , 0 , len (secondLevelInputs ))
1471+ // TODO(roasbeef): only register one of them?
1472+
1473+ secondLevelSweeps := make ([]* justiceTxCtx , 0 , len (secondLevelInputs ))
14431474 for _ , input := range secondLevelInputs {
14441475 sweepTx , err := b .createSweepTx (input )
14451476 if err != nil {
@@ -1456,9 +1487,23 @@ func (b *BreachArbitrator) createJusticeTx(
14561487 return txs , nil
14571488}
14581489
1490+ // justiceTxCtx contains the justice transaction along with other related meta
1491+ // data.
1492+ type justiceTxCtx struct {
1493+ justiceTx * wire.MsgTx
1494+
1495+ sweepAddr lnwallet.AddrWithKey
1496+
1497+ extraTxOut fn.Option [sweep.SweepOutput ]
1498+
1499+ fee btcutil.Amount
1500+
1501+ inputs []input.Input
1502+ }
1503+
14591504// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
1460- func (b * BreachArbitrator ) createSweepTx (inputs ... input. Input ) ( * wire. MsgTx ,
1461- error ) {
1505+ func (b * BreachArbitrator ) createSweepTx (
1506+ inputs ... input. Input ) ( * justiceTxCtx , error ) {
14621507
14631508 if len (inputs ) == 0 {
14641509 return nil , nil
@@ -1481,6 +1526,15 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
14811526 // nLockTime, and output are already included in the TxWeightEstimator.
14821527 weightEstimate .AddP2TROutput ()
14831528
1529+ // If any of our inputs has a resolution blob, then we'll add another
1530+ // P2TR output.
1531+ hasBlobs := fn .Any (func (i input.Input ) bool {
1532+ return i .ResolutionBlob ().IsSome ()
1533+ }, inputs )
1534+ if hasBlobs {
1535+ weightEstimate .AddP2TROutput ()
1536+ }
1537+
14841538 // Next, we iterate over the breached outputs contained in the
14851539 // retribution info. For each, we switch over the witness type such
14861540 // that we contribute the appropriate weight for each input and
@@ -1514,7 +1568,7 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
15141568// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
15151569// spendable outputs by sweeping the funds into a single p2wkh output.
15161570func (b * BreachArbitrator ) sweepSpendableOutputsTxn (txWeight lntypes.WeightUnit ,
1517- inputs ... input.Input ) (* wire. MsgTx , error ) {
1571+ inputs ... input.Input ) (* justiceTxCtx , error ) {
15181572
15191573 // First, we obtain a new public key script from the wallet which we'll
15201574 // sweep the funds to.
@@ -1539,19 +1593,41 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
15391593 }
15401594 txFee := feePerKw .FeeForWeight (txWeight )
15411595
1596+ // At this point, we'll check to see if we have any extra outputs to
1597+ // add from the aux sweeper.
1598+ extraChangeOut := fn .MapOptionZ (
1599+ b .cfg .AuxSweeper ,
1600+ func (aux sweep.AuxSweeper ) fn.Result [sweep.SweepOutput ] {
1601+ return aux .DeriveSweepAddr (inputs , pkScript )
1602+ },
1603+ )
1604+ if err := extraChangeOut .Err (); err != nil {
1605+ return nil , err
1606+ }
1607+
15421608 // TODO(roasbeef): already start to siphon their funds into fees
15431609 sweepAmt := int64 (totalAmt - txFee )
15441610
15451611 // With the fee calculated, we can now create the transaction using the
15461612 // information gathered above and the provided retribution information.
15471613 txn := wire .NewMsgTx (2 )
15481614
1549- // We begin by adding the output to which our funds will be deposited.
1615+ // First, we'll add the extra swep output if it exists, subtracting the
1616+ // amount from the sweep amt.
1617+ extraChangeOut .WhenResult (func (o sweep.SweepOutput ) {
1618+ sweepAmt -= o .Value
1619+
1620+ txn .AddTxOut (& o .TxOut )
1621+ })
1622+
1623+ // Next, we'll add the output to which our funds will be deposited.
15501624 txn .AddTxOut (& wire.TxOut {
15511625 PkScript : pkScript .DeliveryAddress ,
15521626 Value : sweepAmt ,
15531627 })
15541628
1629+ // TODO(roasbeef): add other output change modify sweep amt
1630+
15551631 // Next, we add all of the spendable outputs as inputs to the
15561632 // transaction.
15571633 for _ , inp := range inputs {
@@ -1607,7 +1683,13 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
16071683 }
16081684 }
16091685
1610- return txn , nil
1686+ return & justiceTxCtx {
1687+ justiceTx : txn ,
1688+ sweepAddr : pkScript ,
1689+ extraTxOut : extraChangeOut .Option (),
1690+ fee : txFee ,
1691+ inputs : inputs ,
1692+ }, nil
16111693}
16121694
16131695// RetributionStore handles persistence of retribution states to disk and is
0 commit comments