@@ -820,7 +820,9 @@ func updateBreachInfo(breachInfo *retributionInfo, spends []spend,
820820// haven't already notified about it, we call NotifyBroadcast on the aux sweeper
821821// to generate asset-level proofs.
822822func (b * BreachArbitrator ) notifyConfirmedJusticeTx (spends []spend ,
823- justiceTxs * justiceTxVariants , notifiedTxs map [chainhash.Hash ]bool ) {
823+ justiceTxs * justiceTxVariants ,
824+ historicJusticeTxs map [chainhash.Hash ]* justiceTxCtx ,
825+ notifiedTxs map [chainhash.Hash ]bool ) {
824826
825827 // Check each spend to see if it's from one of our justice txs.
826828 for _ , s := range spends {
@@ -864,6 +866,14 @@ func (b *BreachArbitrator) notifyConfirmedJusticeTx(spends []spend,
864866 }
865867 }
866868
869+ // Check the historic map of all justice tx variants ever
870+ // created. This handles the case where the confirmed tx
871+ // was from a previous rebuild cycle and the current
872+ // justiceTxs has been replaced with newer variants.
873+ if justiceCtx == nil {
874+ justiceCtx = historicJusticeTxs [spendingTxHash ]
875+ }
876+
867877 // If this is one of our justice txs, notify the aux sweeper.
868878 if justiceCtx != nil {
869879 bumpReq := sweep.BumpRequest {
@@ -896,6 +906,28 @@ func (b *BreachArbitrator) notifyConfirmedJusticeTx(spends []spend,
896906 }
897907}
898908
909+ // recordJusticeTxVariants records all non-nil justice tx variants into the
910+ // historic map keyed by their txid. This allows notifyConfirmedJusticeTx to
911+ // match confirmed spends against justice txs from previous rebuild cycles.
912+ func recordJusticeTxVariants (variants * justiceTxVariants ,
913+ history map [chainhash.Hash ]* justiceTxCtx ) {
914+
915+ record := func (jtx * justiceTxCtx ) {
916+ if jtx == nil {
917+ return
918+ }
919+ hash := jtx .justiceTx .TxHash ()
920+ history [hash ] = jtx
921+ }
922+
923+ record (variants .spendAll )
924+ record (variants .spendCommitOuts )
925+ record (variants .spendHTLCs )
926+ for _ , tx := range variants .spendSecondLevelHTLCs {
927+ record (tx )
928+ }
929+ }
930+
899931// exactRetribution is a goroutine which is executed once a contract breach has
900932// been detected by a breachObserver. This function is responsible for
901933// punishing a counterparty for violating the channel contract by sweeping ALL
@@ -951,6 +983,13 @@ func (b *BreachArbitrator) exactRetribution(
951983 // handle duplicate calls idempotently.
952984 notifiedJusticeTxs := make (map [chainhash.Hash ]bool )
953985
986+ // Track all historically created justice tx contexts by their txid.
987+ // This is needed because justiceTxs is rebuilt after each spend
988+ // detection, and the tx that actually confirmed may have been from
989+ // an earlier rebuild cycle. Without this history, we can't match
990+ // the confirmed tx to call NotifyBroadcast on the aux sweeper.
991+ historicJusticeTxs := make (map [chainhash.Hash ]* justiceTxCtx )
992+
954993 // Compute both the total value of funds being swept and the
955994 // amount of funds that were revoked from the counter party.
956995 var totalFunds , revokedFunds btcutil.Amount
@@ -964,6 +1003,7 @@ justiceTxBroadcast:
9641003 brarLog .Errorf ("Unable to create justice tx: %v" , err )
9651004 return
9661005 }
1006+ recordJusticeTxVariants (justiceTxs , historicJusticeTxs )
9671007 finalTx := justiceTxs .spendAll
9681008
9691009 brarLog .Debugf ("Broadcasting justice tx: %v" , lnutils .SpewLogClosure (
@@ -1017,7 +1057,9 @@ Loop:
10171057 // justice transaction, and if so, notify the aux
10181058 // sweeper.
10191059 b .notifyConfirmedJusticeTx (
1020- spends , justiceTxs , notifiedJusticeTxs ,
1060+ spends , justiceTxs ,
1061+ historicJusticeTxs ,
1062+ notifiedJusticeTxs ,
10211063 )
10221064
10231065 // Update the breach info with the new spends.
@@ -1085,6 +1127,44 @@ Loop:
10851127 "height %v), splitting justice tx." ,
10861128 epoch .Height , breachInfo .breachHeight )
10871129
1130+ // Rebuild justice tx variants from the current
1131+ // breach info, which may have been updated by
1132+ // spend detection (e.g. second-level HTLC spends
1133+ // replacing commit-level outputs).
1134+ justiceTxs , err = b .createJusticeTx (
1135+ breachInfo .breachedOutputs ,
1136+ )
1137+ if err != nil {
1138+ brarLog .Errorf ("Unable to recreate " +
1139+ "justice tx for split: %v" , err )
1140+ continue Loop
1141+ }
1142+ recordJusticeTxVariants (
1143+ justiceTxs , historicJusticeTxs ,
1144+ )
1145+
1146+ // Re-attempt the spendAll variant first, in
1147+ // case the breach info was updated since the
1148+ // initial broadcast. This avoids splitting into
1149+ // small txs that can't pay fees when a combined
1150+ // tx would work.
1151+ if justiceTxs .spendAll != nil {
1152+ label := labels .MakeLabel (
1153+ labels .LabelTypeJusticeTransaction ,
1154+ nil ,
1155+ )
1156+ err = b .cfg .PublishTransaction (
1157+ justiceTxs .spendAll .justiceTx ,
1158+ label ,
1159+ )
1160+ if err != nil {
1161+ brarLog .Warnf ("Unable to " +
1162+ "broadcast updated " +
1163+ "spendAll justice " +
1164+ "tx: %v" , err )
1165+ }
1166+ }
1167+
10881168 // Otherwise we'll attempt to publish the two separate
10891169 // justice transactions that sweeps the commitment
10901170 // outputs and the HTLC outputs separately. This is to
@@ -1675,6 +1755,10 @@ func (b *BreachArbitrator) createJusticeTx(
16751755 }
16761756 }
16771757
1758+ brarLog .Infof ("createJusticeTx: %d total inputs (%d commit, " +
1759+ "%d htlc, %d second-level)" , len (allInputs ),
1760+ len (commitInputs ), len (htlcInputs ), len (secondLevelInputs ))
1761+
16781762 var (
16791763 txs = & justiceTxVariants {}
16801764 err error
@@ -1685,31 +1769,42 @@ func (b *BreachArbitrator) createJusticeTx(
16851769 if err != nil {
16861770 return nil , err
16871771 }
1772+ brarLog .Infof ("createJusticeTx: spendAll created successfully " +
1773+ "(%d inputs, txid=%v)" , len (allInputs ),
1774+ txs .spendAll .justiceTx .TxHash ())
16881775
16891776 txs .spendCommitOuts , err = b .createSweepTx (commitInputs ... )
16901777 if err != nil {
16911778 brarLog .Errorf ("could not create sweep tx for commitment " +
16921779 "outputs: %v" , err )
1780+ } else if txs .spendCommitOuts != nil {
1781+ brarLog .Infof ("createJusticeTx: spendCommitOuts created " +
1782+ "successfully (%d inputs)" , len (commitInputs ))
16931783 }
16941784
16951785 txs .spendHTLCs , err = b .createSweepTx (htlcInputs ... )
16961786 if err != nil {
16971787 brarLog .Errorf ("could not create sweep tx for HTLC outputs: %v" ,
16981788 err )
1789+ } else if txs .spendHTLCs != nil {
1790+ brarLog .Infof ("createJusticeTx: spendHTLCs created " +
1791+ "successfully (%d inputs)" , len (htlcInputs ))
16991792 }
17001793
17011794 // TODO(roasbeef): only register one of them?
17021795
17031796 secondLevelSweeps := make ([]* justiceTxCtx , 0 , len (secondLevelInputs ))
1704- for _ , input := range secondLevelInputs {
1797+ for i , input := range secondLevelInputs {
17051798 sweepTx , err := b .createSweepTx (input )
17061799 if err != nil {
17071800 brarLog .Errorf ("could not create sweep tx for " +
1708- "second-level HTLC output: %v" , err )
1801+ "second-level HTLC output %d : %v" , i , err )
17091802
17101803 continue
17111804 }
17121805
1806+ brarLog .Infof ("createJusticeTx: individual second-level " +
1807+ "sweep %d created successfully" , i )
17131808 secondLevelSweeps = append (secondLevelSweeps , sweepTx )
17141809 }
17151810 txs .spendSecondLevelHTLCs = secondLevelSweeps
0 commit comments