Skip to content

Commit d696135

Browse files
committed
tapchannel: update registerAndBroadcastSweep for HTLC sweeps
In this commit, we update `registerAndBroadcastSweep` to be able to handle HTLC sweeps. First, we only need to set the anchor for first level sweeps. We weren't able to sign the 2nd level sweeps earlier as we didn't know the txid of the transaction that swept them. Now that we're about to broadcast, we know the sweeping transaction so we can set the prevID properly, then sign the sweeping transaction. If the sweeper is broadcasting _just_ a second level txn with an asset, then we won't have an extra change output. This transaction also already has its internal key bound.
1 parent 3d49e1a commit d696135

File tree

1 file changed

+106
-17
lines changed

1 file changed

+106
-17
lines changed

tapchannel/aux_sweeper.go

Lines changed: 106 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,20 @@ type vPktsWithInput struct {
446446
tapSigDesc lfn.Option[cmsg.TapscriptSigDesc]
447447
}
448448

449+
// isPresigned returns true if the vPktsWithInput is presigned. This will be the
450+
// for an HTLC spent directly from our local commitment transaction.
451+
func (v vPktsWithInput) isPresigned() bool {
452+
witType := v.btcInput.WitnessType()
453+
switch witType {
454+
case input.TaprootHtlcAcceptedLocalSuccess:
455+
return true
456+
case input.TaprootHtlcLocalOfferedTimeout:
457+
return true
458+
default:
459+
return false
460+
}
461+
}
462+
449463
// sweepVpkts contains the set of vPkts needed for sweeping an output. Most
450464
// outputs will only have the first level specified. The second level is needed
451465
// for HTLC outputs on our local commitment transaction.
@@ -487,6 +501,27 @@ func (s sweepVpkts) allPkts() []*tappsbt.VPacket {
487501
return append(s.firstLevelPkts(), s.secondLevelPkts()...)
488502
}
489503

504+
// allVpktsWithInput returns a slice of all vPktsWithInput.
505+
func (s sweepVpkts) allVpktsWithInput() []vPktsWithInput {
506+
return append(s.firstLevel, s.secondLevel...)
507+
}
508+
509+
// directSpendPkts returns the slice of all vPkts that are a direct spend from
510+
// the commitment transaction. This excludes vPkts that are the pre-signed 2nd
511+
// level transaction variant.
512+
func (s sweepVpkts) directSpendPkts() []*tappsbt.VPacket {
513+
directSpends := lfn.Filter(func(vi vPktsWithInput) bool {
514+
return !vi.isPresigned()
515+
}, s.allVpktsWithInput())
516+
directPkts := fn.FlatMap(
517+
directSpends, func(v vPktsWithInput) []*tappsbt.VPacket {
518+
return v.vPkts
519+
},
520+
)
521+
522+
return directPkts
523+
}
524+
490525
// createAndSignSweepVpackets creates vPackets that sweep the funds from the
491526
// channel to the wallet, and then signs them as well.
492527
func (a *AuxSweeper) createAndSignSweepVpackets(
@@ -1726,6 +1761,7 @@ func (a *AuxSweeper) resolveContract(
17261761
default:
17271762
return lfn.Errf[returnType]("unknown resolution type: %v",
17281763
req.Type)
1764+
// TODO(roasbeef): need to do HTLC revocation casesj:w
17291765
}
17301766

17311767
// The input proofs above were made originally using the fake commit tx
@@ -1980,6 +2016,7 @@ func prepVpkts(bRes lfn.Result[blobWithWitnessInfo],
19802016
// none of the inputs have any resolution blobs. Then an empty slice will be
19812017
// returned.
19822018
func extractInputVPackets(inputs []input.Input) lfn.Result[sweepVpkts] {
2019+
19832020
type returnType = sweepVpkts
19842021

19852022
// Otherwise, we'll extract the set of resolution blobs from the inputs
@@ -2207,34 +2244,86 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
22072244
return nil
22082245
}
22092246

2210-
ourSweepOutput, err := req.ExtraTxOut.UnwrapOrErr(
2211-
fmt.Errorf("extra tx out not populated"),
2247+
// If this is a transaction that's only sweeping HTLC outputs via a
2248+
// pre-signed transaction, then we won't actually have an extra sweep
2249+
// output.
2250+
err = lfn.MapOptionZ(
2251+
req.ExtraTxOut,
2252+
func(extraTxOut sweep.SweepOutput) error {
2253+
ourSweepOutput, err := req.ExtraTxOut.UnwrapOrErr(
2254+
fmt.Errorf("extra tx out not populated"),
2255+
)
2256+
if err != nil {
2257+
return err
2258+
}
2259+
iKey, err := ourSweepOutput.InternalKey.UnwrapOrErr(
2260+
fmt.Errorf("internal key not populated"),
2261+
)
2262+
if err != nil {
2263+
return err
2264+
}
2265+
2266+
// We'll also use the passed in context to set the
2267+
// anchor key again for all the vOuts, but only for
2268+
// first level vPkts, as second level packets already
2269+
// commit to the internal key of the vOut.
2270+
vPkts := vPkts.directSpendPkts()
2271+
for idx := range vPkts {
2272+
for _, vOut := range vPkts[idx].Outputs {
2273+
vOut.SetAnchorInternalKey(
2274+
iKey,
2275+
a.cfg.ChainParams.HDCoinType,
2276+
)
2277+
}
2278+
}
2279+
2280+
return nil
2281+
},
22122282
)
22132283
if err != nil {
22142284
return err
22152285
}
2216-
internalKey, err := ourSweepOutput.InternalKey.UnwrapOrErr(
2217-
fmt.Errorf("internal key not populated"),
2218-
)
2219-
if err != nil {
2220-
return err
2286+
2287+
// For any second level outputs we're sweeping, we'll need to sign for
2288+
// it, as now we know the txid of the sweeping transaction.
2289+
for _, sweepSet := range vPkts.secondLevel {
2290+
for _, vPkt := range sweepSet.vPkts {
2291+
for _, vIn := range vPkt.Inputs {
2292+
vIn.PrevID.OutPoint = sweepSet.btcInput.OutPoint()
2293+
}
2294+
2295+
for _, vOut := range vPkt.Outputs {
2296+
vOut.Asset.PrevWitnesses[0].PrevID.OutPoint = sweepSet.btcInput.OutPoint()
2297+
}
2298+
}
22212299
}
22222300

2223-
log.Infof("Using %x for internal key: ",
2224-
internalKey.PubKey.SerializeCompressed())
2301+
// If we have second level vPkts, then we'll need to sign them here, as
2302+
// now we know the input we're spending which was set above.
2303+
for _, sweepSet := range vPkts.secondLevel {
2304+
tapSigDesc, err := sweepSet.tapSigDesc.UnwrapOrErr(
2305+
fmt.Errorf("tap sig desc not populated"),
2306+
)
2307+
if err != nil {
2308+
return err
2309+
}
22252310

2226-
// We'll also use the passed in context to set the anchor key again for
2227-
// all the vOuts.
2228-
for idx := range vPkts.firstLevelPkts() {
2229-
for _, vOut := range vPkts.firstLevelPkts()[idx].Outputs {
2230-
vOut.SetAnchorInternalKey(
2231-
internalKey, a.cfg.ChainParams.HDCoinType,
2232-
)
2311+
err = a.signSweepVpackets(
2312+
sweepSet.vPkts, *sweepSet.btcInput.SignDesc(),
2313+
tapSigDesc.TapTweak.Val, tapSigDesc.CtrlBlock.Val,
2314+
lfn.None[lnwallet.AuxSigDesc](),
2315+
lfn.None[uint32](),
2316+
)
2317+
if err != nil {
2318+
return fmt.Errorf("unable to sign second level "+
2319+
"vPkts: %w", err)
22332320
}
22342321
}
22352322

22362323
// Now that we have our vPkts, we'll re-create the output commitments.
2237-
outCommitments, err := tapsend.CreateOutputCommitments(vPkts.allPkts())
2324+
outCommitments, err := tapsend.CreateOutputCommitments(
2325+
vPkts.allPkts(),
2326+
)
22382327
if err != nil {
22392328
return fmt.Errorf("unable to create output "+
22402329
"commitments: %w", err)

0 commit comments

Comments
 (0)