Skip to content

Commit 7df3b77

Browse files
Roasbeefguggero
authored andcommitted
contractcourt: integration aux sweeper to breach arb
Similar to the sweeper, when we're about to make a new breach transaction, we ask the sweeper for a new change address, if it has one. Then when we go to publish, we notify broadcast.
1 parent 35d2873 commit 7df3b77

File tree

3 files changed

+100
-17
lines changed

3 files changed

+100
-17
lines changed

contractcourt/breach_arbitrator.go

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
13731402
type 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.
15151569
func (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

contractcourt/breach_arbitrator_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,16 +1230,16 @@ func TestBreachCreateJusticeTx(t *testing.T) {
12301230

12311231
// The spendAll tx should be spending all the outputs. This is the
12321232
// "regular" justice transaction type.
1233-
require.Len(t, justiceTxs.spendAll.TxIn, len(breachedOutputs))
1233+
require.Len(t, justiceTxs.spendAll.justiceTx.TxIn, len(breachedOutputs))
12341234

12351235
// The spendCommitOuts tx should be spending the 4 types of commit outs
12361236
// (note that in practice there will be at most two commit outputs per
12371237
// commit, but we test all 4 types here).
1238-
require.Len(t, justiceTxs.spendCommitOuts.TxIn, 4)
1238+
require.Len(t, justiceTxs.spendCommitOuts.justiceTx.TxIn, 4)
12391239

12401240
// Check that the spendHTLCs tx is spending the two revoked commitment
12411241
// level HTLC output types.
1242-
require.Len(t, justiceTxs.spendHTLCs.TxIn, 2)
1242+
require.Len(t, justiceTxs.spendHTLCs.justiceTx.TxIn, 2)
12431243

12441244
// Finally, check that the spendSecondLevelHTLCs txs are spending the
12451245
// second level type.

server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,7 @@ func newServer(cfg *Config, listenAddrs []net.Addr,
11811181
Store: contractcourt.NewRetributionStore(
11821182
dbs.ChanStateDB,
11831183
),
1184+
AuxSweeper: s.implCfg.AuxSweeper,
11841185
},
11851186
)
11861187

0 commit comments

Comments
 (0)