@@ -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 " +
@@ -1371,10 +1400,10 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
13711400// spend the to_local output and commitment level HTLC outputs separately,
13721401// before the CSV locks expire.
13731402type justiceTxVariants struct {
1374- spendAll * wire. MsgTx
1375- spendCommitOuts * wire. MsgTx
1376- spendHTLCs * wire. MsgTx
1377- spendSecondLevelHTLCs []* wire. MsgTx
1403+ spendAll * justiceTxCtx
1404+ spendCommitOuts * justiceTxCtx
1405+ spendHTLCs * justiceTxCtx
1406+ spendSecondLevelHTLCs []* justiceTxCtx
13781407}
13791408
13801409// createJusticeTx creates transactions which exacts "justice" by sweeping ALL
@@ -1438,7 +1467,9 @@ func (b *BreachArbitrator) createJusticeTx(
14381467 err )
14391468 }
14401469
1441- secondLevelSweeps := make ([]* wire.MsgTx , 0 , len (secondLevelInputs ))
1470+ // TODO(roasbeef): only register one of them?
1471+
1472+ secondLevelSweeps := make ([]* justiceTxCtx , 0 , len (secondLevelInputs ))
14421473 for _ , input := range secondLevelInputs {
14431474 sweepTx , err := b .createSweepTx (input )
14441475 if err != nil {
@@ -1455,9 +1486,23 @@ func (b *BreachArbitrator) createJusticeTx(
14551486 return txs , nil
14561487}
14571488
1489+ // justiceTxCtx contains the justice transaction along with other related meta
1490+ // data.
1491+ type justiceTxCtx struct {
1492+ justiceTx * wire.MsgTx
1493+
1494+ sweepAddr lnwallet.AddrWithKey
1495+
1496+ extraTxOut fn.Option [sweep.SweepOutput ]
1497+
1498+ fee btcutil.Amount
1499+
1500+ inputs []input.Input
1501+ }
1502+
14581503// createSweepTx creates a tx that sweeps the passed inputs back to our wallet.
1459- func (b * BreachArbitrator ) createSweepTx (inputs ... input. Input ) ( * wire. MsgTx ,
1460- error ) {
1504+ func (b * BreachArbitrator ) createSweepTx (
1505+ inputs ... input. Input ) ( * justiceTxCtx , error ) {
14611506
14621507 if len (inputs ) == 0 {
14631508 return nil , nil
@@ -1480,6 +1525,15 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
14801525 // nLockTime, and output are already included in the TxWeightEstimator.
14811526 weightEstimate .AddP2TROutput ()
14821527
1528+ // If any of our inputs has a resolution blob, then we'll add another
1529+ // P2TR output.
1530+ hasBlobs := fn .Any (func (i input.Input ) bool {
1531+ return i .ResolutionBlob ().IsSome ()
1532+ }, inputs )
1533+ if hasBlobs {
1534+ weightEstimate .AddP2TROutput ()
1535+ }
1536+
14831537 // Next, we iterate over the breached outputs contained in the
14841538 // retribution info. For each, we switch over the witness type such
14851539 // that we contribute the appropriate weight for each input and
@@ -1513,7 +1567,7 @@ func (b *BreachArbitrator) createSweepTx(inputs ...input.Input) (*wire.MsgTx,
15131567// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
15141568// spendable outputs by sweeping the funds into a single p2wkh output.
15151569func (b * BreachArbitrator ) sweepSpendableOutputsTxn (txWeight lntypes.WeightUnit ,
1516- inputs ... input.Input ) (* wire. MsgTx , error ) {
1570+ inputs ... input.Input ) (* justiceTxCtx , error ) {
15171571
15181572 // First, we obtain a new public key script from the wallet which we'll
15191573 // sweep the funds to.
@@ -1538,19 +1592,41 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
15381592 }
15391593 txFee := feePerKw .FeeForWeight (txWeight )
15401594
1595+ // At this point, we'll check to see if we have any extra outputs to
1596+ // add from the aux sweeper.
1597+ extraChangeOut := fn .MapOptionZ (
1598+ b .cfg .AuxSweeper ,
1599+ func (aux sweep.AuxSweeper ) fn.Result [sweep.SweepOutput ] {
1600+ return aux .DeriveSweepAddr (inputs , pkScript )
1601+ },
1602+ )
1603+ if err := extraChangeOut .Err (); err != nil {
1604+ return nil , err
1605+ }
1606+
15411607 // TODO(roasbeef): already start to siphon their funds into fees
15421608 sweepAmt := int64 (totalAmt - txFee )
15431609
15441610 // With the fee calculated, we can now create the transaction using the
15451611 // information gathered above and the provided retribution information.
15461612 txn := wire .NewMsgTx (2 )
15471613
1548- // We begin by adding the output to which our funds will be deposited.
1614+ // First, we'll add the extra swep output if it exists, subtracting the
1615+ // amount from the sweep amt.
1616+ extraChangeOut .WhenResult (func (o sweep.SweepOutput ) {
1617+ sweepAmt -= o .Value
1618+
1619+ txn .AddTxOut (& o .TxOut )
1620+ })
1621+
1622+ // Next, we'll add the output to which our funds will be deposited.
15491623 txn .AddTxOut (& wire.TxOut {
15501624 PkScript : pkScript .DeliveryAddress ,
15511625 Value : sweepAmt ,
15521626 })
15531627
1628+ // TODO(roasbeef): add other output change modify sweep amt
1629+
15541630 // Next, we add all of the spendable outputs as inputs to the
15551631 // transaction.
15561632 for _ , inp := range inputs {
@@ -1606,7 +1682,13 @@ func (b *BreachArbitrator) sweepSpendableOutputsTxn(txWeight lntypes.WeightUnit,
16061682 }
16071683 }
16081684
1609- return txn , nil
1685+ return & justiceTxCtx {
1686+ justiceTx : txn ,
1687+ sweepAddr : pkScript ,
1688+ extraTxOut : extraChangeOut .Option (),
1689+ fee : txFee ,
1690+ inputs : inputs ,
1691+ }, nil
16101692}
16111693
16121694// RetributionStore handles persistence of retribution states to disk and is
0 commit comments