@@ -430,6 +430,91 @@ func (a *AuxSweeper) signSweepVpackets(vPackets []*tappsbt.VPacket,
430430 return nil
431431}
432432
433+ // vPktsWithInput couples a vPkt along with the input that contained it.
434+ type vPktsWithInput struct {
435+ // btcInput is the Bitcoin that the vPkt will the spending from (on the
436+ // TAP layer).
437+ btcInput input.Input
438+
439+ // vPkts is the set of vPacket that will be used to spend the input.
440+ vPkts []* tappsbt.VPacket
441+
442+ // tapSigDesc houses the information we'll need to re-sign the vPackets
443+ // above. Note that this is only set if this is a second level packet.
444+ tapSigDesc lfn.Option [cmsg.TapscriptSigDesc ]
445+ }
446+
447+ // isPresigned returns true if the vPktsWithInput is presigned. This will be the
448+ // for an HTLC spent directly from our local commitment transaction.
449+ func (v vPktsWithInput ) isPresigned () bool {
450+ witType := v .btcInput .WitnessType ()
451+ switch witType {
452+ case input .TaprootHtlcAcceptedLocalSuccess :
453+ return true
454+ case input .TaprootHtlcLocalOfferedTimeout :
455+ return true
456+ default :
457+ return false
458+ }
459+ }
460+
461+ // sweepVpkts contains the set of vPkts needed for sweeping an output. Most
462+ // outputs will only have the first level specified. The second level is needed
463+ // for HTLC outputs on our local commitment transaction.
464+ type sweepVpkts struct {
465+ // firstLevel houses vPackets that are used to sweep outputs directly
466+ // from the commitment transaction.
467+ firstLevel []vPktsWithInput
468+
469+ // secondLevel is used to sweep outputs that are created by second level
470+ // HTLC transactions.
471+ secondLevel []vPktsWithInput
472+ }
473+
474+ // firstLevelPkts returns a slice of the first level pkts.
475+ func (s sweepVpkts ) firstLevelPkts () []* tappsbt.VPacket {
476+ return fn .FlatMap (
477+ s .firstLevel , func (v vPktsWithInput ) []* tappsbt.VPacket {
478+ return v .vPkts
479+ },
480+ )
481+ }
482+
483+ // secondLevelPkts returns a slice of the second level pkts.
484+ func (s sweepVpkts ) secondLevelPkts () []* tappsbt.VPacket {
485+ return fn .FlatMap (
486+ s .secondLevel , func (v vPktsWithInput ) []* tappsbt.VPacket {
487+ return v .vPkts
488+ },
489+ )
490+ }
491+
492+ // allPkts returns a slice of both the first and second level pkts.
493+ func (s sweepVpkts ) allPkts () []* tappsbt.VPacket {
494+ return append (s .firstLevelPkts (), s .secondLevelPkts ()... )
495+ }
496+
497+ // allVpktsWithInput returns a slice of all vPktsWithInput.
498+ func (s sweepVpkts ) allVpktsWithInput () []vPktsWithInput {
499+ return append (s .firstLevel , s .secondLevel ... )
500+ }
501+
502+ // directSpendPkts returns the slice of all vPkts that are a direct spend from
503+ // the commitment transaction. This excludes vPkts that are the pre-signed 2nd
504+ // level transaction variant.
505+ func (s sweepVpkts ) directSpendPkts () []* tappsbt.VPacket {
506+ directSpends := lfn .Filter (func (vi vPktsWithInput ) bool {
507+ return ! vi .isPresigned ()
508+ }, s .allVpktsWithInput ())
509+ directPkts := fn .FlatMap (
510+ directSpends , func (v vPktsWithInput ) []* tappsbt.VPacket {
511+ return v .vPkts
512+ },
513+ )
514+
515+ return directPkts
516+ }
517+
433518// createAndSignSweepVpackets creates vPackets that sweep the funds from the
434519// channel to the wallet, and then signs them as well.
435520func (a * AuxSweeper ) createAndSignSweepVpackets (
@@ -641,6 +726,8 @@ func remoteHtlcTimeoutSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
641726 return lfn.Err [tapscriptSweepDescs ](err )
642727 }
643728
729+ // TODO(roasbeef): use GenTaprootHtlcScript instead?
730+
644731 // Now that we have the script tree, we'll make the control block needed
645732 // to spend it, but taking the revoked path.
646733 ctrlBlock , err := htlcScriptTree .CtrlBlockForPath (
@@ -1223,8 +1310,8 @@ func importOutputProofs(scid lnwire.ShortChannelID,
12231310 spew .Sdump (inputProofLocator ))
12241311
12251312 // Before we combine the proofs below, we'll be sure to update
1226- // the transition proof to include the proper block+merkle
1227- // proof information.
1313+ // the transition proof to include the proper block+merkle proof
1314+ // information.
12281315 blockHash , err := chainBridge .GetBlockHash (
12291316 ctxb , int64 (scid .BlockHeight ),
12301317 )
@@ -1815,8 +1902,8 @@ func newBlobWithWitnessInfo(i input.Input) lfn.Result[blobWithWitnessInfo] {
18151902 secondLevel bool
18161903 )
18171904 switch i .WitnessType () {
1818-
18191905 // This is the case when we're sweeping the HTLC output on our local
1906+
18201907 // commitment transaction via a second level HTLC.
18211908 //
18221909 // The final witness stack is:
@@ -1873,40 +1960,121 @@ func newBlobWithWitnessInfo(i input.Input) lfn.Result[blobWithWitnessInfo] {
18731960 })
18741961}
18751962
1963+ // prepVpkts decodes the set of vPkts, supplementing them as needed to ensure
1964+ // all inputs can be swept properly.
1965+ func prepVpkts (bRes lfn.Result [blobWithWitnessInfo ],
1966+ secondLevel bool ) (* vPktsWithInput , error ) {
1967+
1968+ b , err := bRes .Unpack ()
1969+ if err != nil {
1970+ return nil , err
1971+ }
1972+
1973+ var res cmsg.ContractResolution
1974+ err = res .Decode (bytes .NewReader (b .resolutionBlob ))
1975+ if err != nil {
1976+ return nil , err
1977+ }
1978+
1979+ // For each vPacket, if we have a preimage to insert, then we'll we'll
1980+ // update the witness to insert the preimage at the correct index.
1981+ var tapSigDesc lfn.Option [cmsg.TapscriptSigDesc ]
1982+ pkts := res .Vpkts1 ()
1983+ if secondLevel {
1984+ pkts = res .Vpkts2 ()
1985+ tapSigDesc = res .SigDescs ()
1986+ }
1987+
1988+ err = lfn .MapOptionZ (b .preimageInfo , func (p preimageDesc ) error {
1989+ for _ , pkt := range pkts {
1990+ newAsset := pkt .Outputs [0 ].Asset
1991+
1992+ prevWitness := newAsset .PrevWitnesses [0 ].TxWitness
1993+ prevWitness = slices .Insert (
1994+ prevWitness , p .witnessIndex ,
1995+ p .preimage [:],
1996+ )
1997+ err := newAsset .UpdateTxWitness (0 , prevWitness )
1998+ if err != nil {
1999+ return err
2000+ }
2001+ }
2002+
2003+ return nil
2004+ })
2005+ if err != nil {
2006+ return nil , err
2007+ }
2008+
2009+ return & vPktsWithInput {
2010+ vPkts : pkts ,
2011+ btcInput : b .input ,
2012+ tapSigDesc : tapSigDesc ,
2013+ }, nil
2014+ }
2015+
18762016// extractInputVPackets extracts the vPackets from the inputs passed in. If
18772017// none of the inputs have any resolution blobs. Then an empty slice will be
18782018// returned.
1879- func extractInputVPackets (inputs []input.Input ) lfn.Result [[]* tappsbt.VPacket ] {
1880- type returnType = []* tappsbt.VPacket
1881-
1882- // Otherwise, we'll extract the set of resolution blobs from the inputs
2019+ func extractInputVPackets (inputs []input.Input ) lfn.Result [sweepVpkts ] {
2020+ // First, we'll extract the set of resolution blobs from the inputs
18832021 // passed in.
18842022 relevantInputs := fn .Filter (inputs , func (i input.Input ) bool {
18852023 return i .ResolutionBlob ().IsSome ()
18862024 })
1887- resolutionBlobs := fn .Map (relevantInputs , func (i input.Input ) tlv.Blob {
1888- // We already know this has a blob from the filter above.
1889- return i .ResolutionBlob ().UnwrapOr (nil )
1890- })
2025+ resolutionInfo := fn .Map (
2026+ relevantInputs , newBlobWithWitnessInfo ,
2027+ )
18912028
1892- // With our set of resolution inputs extracted, we'll now decode them
1893- // in the vPackets we'll use to generate the output to addr.
1894- vPkts , err := fn .FlatMapErr (
1895- resolutionBlobs ,
1896- func (b tlv.Blob ) ([]* tappsbt.VPacket , error ) {
1897- var res cmsg.ContractResolution
1898- if err := res .Decode (bytes .NewReader (b )); err != nil {
1899- return nil , err
1900- }
2029+ firstLevelSweeps := lfn .Filter (
2030+ func (info lfn.Result [blobWithWitnessInfo ]) bool {
2031+ var secondLevel bool
2032+ info .WhenResult (func (i blobWithWitnessInfo ) {
2033+ secondLevel = i .secondLevel
2034+ })
19012035
1902- return res . Vpkts1 (), nil
2036+ return ! secondLevel
19032037 },
2038+ resolutionInfo ,
19042039 )
1905- if err != nil {
1906- return lfn.Err [returnType ](err )
2040+ secondLevelSweeps := lfn .Filter (
2041+ func (info lfn.Result [blobWithWitnessInfo ]) bool {
2042+ var secondLevel bool
2043+ info .WhenResult (func (i blobWithWitnessInfo ) {
2044+ secondLevel = i .secondLevel
2045+ })
2046+
2047+ return secondLevel
2048+ },
2049+ resolutionInfo ,
2050+ )
2051+
2052+ // With our set of resolution inputs extracted, we'll now decode them in
2053+ // the vPackets we'll use to generate the output to addr.
2054+ var vPkts1 []vPktsWithInput
2055+ for _ , bRes := range firstLevelSweeps {
2056+ vpkt , err := prepVpkts (bRes , false )
2057+ if err != nil {
2058+ return lfn.Err [sweepVpkts ](err )
2059+ }
2060+
2061+ vPkts1 = append (vPkts1 , * vpkt )
19072062 }
19082063
1909- return lfn .Ok (vPkts )
2064+ var vPkts2 []vPktsWithInput
2065+ for _ , bRes := range secondLevelSweeps {
2066+ vpkt , err := prepVpkts (bRes , true )
2067+ if err != nil {
2068+ return lfn.Err [sweepVpkts ](err )
2069+ }
2070+
2071+ vPkts2 = append (vPkts2 , * vpkt )
2072+ }
2073+
2074+ return lfn .Ok (sweepVpkts {
2075+ firstLevel : vPkts1 ,
2076+ secondLevel : vPkts2 ,
2077+ })
19102078}
19112079
19122080// sweepContracts takes a set of inputs, and the change address we'd use to
@@ -1930,13 +2098,25 @@ func (a *AuxSweeper) sweepContracts(inputs []input.Input,
19302098
19312099 // Now that we know we have a relevant input set, extract all the
19322100 // vPackets from the inputs.
1933- vPkts , err := extractInputVPackets (inputs ).Unpack ()
2101+ sPkts , err := extractInputVPackets (inputs ).Unpack ()
19342102 if err != nil {
19352103 return lfn.Err [returnType ](err )
19362104 }
19372105
19382106 log .Infof ("Generating anchor output for vpkts=%v" ,
1939- limitSpewer .Sdump (vPkts ))
2107+ limitSpewer .Sdump (sPkts ))
2108+
2109+ // If this is a sweep from the local commitment transaction. Then we'll
2110+ // have both the first and second level sweeps. However for the first
2111+ // sweep, it's a broadcast of a pre-signed transaction, so we don't need
2112+ // an anchor output for those.
2113+ directPkts := sPkts .directSpendPkts ()
2114+
2115+ // If there're no direct level vPkts, then we can just return a nil
2116+ // error as we don't have a real sweep output to create.
2117+ if len (directPkts ) == 0 {
2118+ return lfn.Err [sweep.SweepOutput ](nil )
2119+ }
19402120
19412121 // At this point, now that we're about to generate a new output, we'll
19422122 // need an internal key, so we can update all the vPkts.
@@ -1949,17 +2129,33 @@ func (a *AuxSweeper) sweepContracts(inputs []input.Input,
19492129 if err != nil {
19502130 return lfn.Err [returnType ](err )
19512131 }
1952- for idx := range vPkts {
1953- for _ , vOut := range vPkts [idx ].Outputs {
2132+ for idx := range directPkts {
2133+ for _ , vOut := range directPkts [idx ].Outputs {
19542134 vOut .SetAnchorInternalKey (
19552135 internalKey , a .cfg .ChainParams .HDCoinType ,
19562136 )
19572137 }
19582138 }
19592139
2140+ // For any second level outputs we're sweeping, we'll need to sign for
2141+ // it, as now we know the txid of the sweeping transaction. We'll do
2142+ // this again when we register for the final broadcast, we we need to
2143+ // sign the right prevIDs.
2144+ for _ , sweepSet := range sPkts .secondLevel {
2145+ for _ , vPkt := range sweepSet .vPkts {
2146+ prevOut := sweepSet .btcInput .OutPoint ()
2147+ for _ , vIn := range vPkt .Inputs {
2148+ vIn .PrevID .OutPoint = prevOut
2149+ }
2150+ for _ , vOut := range vPkt .Outputs {
2151+ vOut .Asset .PrevWitnesses [0 ].PrevID .OutPoint = prevOut //nolint:lll
2152+ }
2153+ }
2154+ }
2155+
19602156 // Now that we have our set of resolutions, we'll make a new commitment
19612157 // out of all the vPackets contained.
1962- outCommitments , err := tapsend .CreateOutputCommitments (vPkts )
2158+ outCommitments , err := tapsend .CreateOutputCommitments (directPkts )
19632159 if err != nil {
19642160 return lfn .Errf [returnType ]("unable to create " +
19652161 "output commitments: %w" , err )
@@ -2039,7 +2235,7 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
20392235
20402236 // If we don't have any vPackets that had our resolution data in them,
20412237 // then we can exit early.
2042- if len (vPkts ) == 0 {
2238+ if len (vPkts . firstLevel ) == 0 && len ( vPkts . secondLevel ) == 0 {
20432239 log .Infof ("Sweep request had no vPkts, exiting" )
20442240 return nil
20452241 }
@@ -2062,16 +2258,16 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
20622258
20632259 // We'll also use the passed in context to set the anchor key again for
20642260 // all the vOuts.
2065- for idx := range vPkts {
2066- for _ , vOut := range vPkts [idx ].Outputs {
2261+ for idx := range vPkts . firstLevelPkts () {
2262+ for _ , vOut := range vPkts . firstLevelPkts () [idx ].Outputs {
20672263 vOut .SetAnchorInternalKey (
20682264 internalKey , a .cfg .ChainParams .HDCoinType ,
20692265 )
20702266 }
20712267 }
20722268
20732269 // Now that we have our vPkts, we'll re-create the output commitments.
2074- outCommitments , err := tapsend .CreateOutputCommitments (vPkts )
2270+ outCommitments , err := tapsend .CreateOutputCommitments (vPkts . allPkts () )
20752271 if err != nil {
20762272 return fmt .Errorf ("unable to create output " +
20772273 "commitments: %w" , err )
@@ -2093,15 +2289,16 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
20932289 //
20942290 // TODO(roasbeef): base off allocations? then can serialize, then
20952291 // re-use the logic
2096- for idx := range vPkts {
2097- vPkt := vPkts [idx ]
2292+ allVpkts := vPkts .allPkts ()
2293+ for idx := range allVpkts {
2294+ vPkt := allVpkts [idx ]
20982295 for outIdx := range vPkt .Outputs {
20992296 exclusionCreator := sweepExclusionProofGen (
21002297 changeInternalKey ,
21012298 )
21022299
21032300 proofSuffix , err := tapsend .CreateProofSuffixCustom (
2104- sweepTx , vPkt , outCommitments , outIdx , vPkts ,
2301+ sweepTx , vPkt , outCommitments , outIdx , allVpkts ,
21052302 exclusionCreator ,
21062303 )
21072304 if err != nil {
@@ -2121,7 +2318,7 @@ func (a *AuxSweeper) registerAndBroadcastSweep(req *sweep.BumpRequest,
21212318 // We pass false for the last arg as we already updated our suffix
21222319 // proofs here.
21232320 return shipChannelTxn (
2124- a .cfg .TxSender , sweepTx , outCommitments , vPkts , int64 (fee ),
2321+ a .cfg .TxSender , sweepTx , outCommitments , allVpkts , int64 (fee ),
21252322 )
21262323}
21272324
0 commit comments