Skip to content

Commit 59c2cf7

Browse files
committed
tapchannel: update sweepContracts for HTLC sweeps
In this commit, we add some intermediate functions and types needed to properly handle HTLC sweeps. We now use the preimage info added in a prior commit to make sure that once we learn of the preimage (after vPkt creation, but before publish), we'll properly insert it in the correct location. In sweepContracts, when we go to create the new anchor change output, we'll make sure to omit any second level transactions, as they already have a destination location specified.
1 parent 254224e commit 59c2cf7

File tree

1 file changed

+170
-24
lines changed

1 file changed

+170
-24
lines changed

tapchannel/aux_sweeper.go

Lines changed: 170 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,61 @@ func (a *AuxSweeper) signSweepVpackets(vPackets []*tappsbt.VPacket,
430430
return nil
431431
}
432432

433+
// applySignDescToVIn 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+
// sweepVpkts contains the set of vPkts needed for sweeping an output. Most
448+
// outputs will only have the first level specified. The second level is needed
449+
// for HTLC outputs on our local commitment transaction.
450+
type sweepVpkts struct {
451+
// firstLevel houses vPackets that are used to sweep outputs directly
452+
// from the commitment transaction.
453+
firstLevel []vPktsWithInput
454+
455+
// secondLevel is used to sweep outputs that are created by second level
456+
// HTLC transactions.
457+
secondLevel []vPktsWithInput
458+
}
459+
460+
// isEmpty returns true if the sweepVpkts is empty.
461+
func (s sweepVpkts) isEmpty() bool {
462+
return len(s.firstLevel) == 0 && len(s.secondLevel) == 0
463+
}
464+
465+
// firstLevelPkts returns a slice of the first level pkts.
466+
func (s sweepVpkts) firstLevelPkts() []*tappsbt.VPacket {
467+
return fn.FlatMap(
468+
s.firstLevel, func(v vPktsWithInput) []*tappsbt.VPacket {
469+
return v.vPkts
470+
},
471+
)
472+
}
473+
474+
// secondLevelPkts returns a slice of the second level pkts.
475+
func (s sweepVpkts) secondLevelPkts() []*tappsbt.VPacket {
476+
return fn.FlatMap(
477+
s.secondLevel, func(v vPktsWithInput) []*tappsbt.VPacket {
478+
return v.vPkts
479+
},
480+
)
481+
}
482+
483+
// allPkts returns a slice of both the first and second level pkts.
484+
func (s sweepVpkts) allPkts() []*tappsbt.VPacket {
485+
return append(s.firstLevelPkts(), s.secondLevelPkts()...)
486+
}
487+
433488
// createAndSignSweepVpackets creates vPackets that sweep the funds from the
434489
// channel to the wallet, and then signs them as well.
435490
func (a *AuxSweeper) createAndSignSweepVpackets(
@@ -1847,42 +1902,122 @@ func newBlobWithWitnessInfo(i input.Input) lfn.Result[blobWithWitnessInfo] {
18471902
secondLevel: secondLevel,
18481903
})
18491904
}
1905+
1906+
// prepVpkts decodes the set of vPkts, supplementing them as needed to ensure
1907+
// all inputs can be swept properly.
1908+
func prepVpkts(bRes lfn.Result[blobWithWitnessInfo],
1909+
secondLevel bool) (*vPktsWithInput, error) {
1910+
1911+
b, err := bRes.Unpack()
1912+
if err != nil {
1913+
return nil, err
1914+
}
1915+
var res cmsg.ContractResolution
1916+
1917+
err = res.Decode(bytes.NewReader(b.resolutionBlob))
1918+
if err != nil {
1919+
return nil, err
1920+
}
1921+
1922+
// For each vPacket, if we have a preimage to insert, then we'll we'll
1923+
// update the witness to insert the preimage at the correct index.
1924+
var tapSigDesc lfn.Option[cmsg.TapscriptSigDesc]
1925+
pkts := res.Vpkts1()
1926+
if secondLevel {
1927+
pkts = res.Vpkts2()
1928+
}
1929+
1930+
b.preimageInfo.WhenSome(func(p preimageDesc) {
1931+
vIns := fn.FlatMap(
1932+
pkts,
1933+
func(vPkt *tappsbt.VPacket) []*tappsbt.VInput {
1934+
return vPkt.Inputs
1935+
},
1936+
)
1937+
1938+
for _, vIn := range vIns {
1939+
prevWitness := vIn.Asset().PrevWitnesses[0].TxWitness
1940+
vIn.Asset().PrevWitnesses[0].TxWitness = slices.Insert(
1941+
prevWitness, p.witnessIndex,
1942+
p.preimage[:],
1943+
)
1944+
}
1945+
})
1946+
1947+
return &vPktsWithInput{
1948+
vPkts: pkts,
1949+
btcInput: b.input,
1950+
tapSigDesc: tapSigDesc,
1951+
}, nil
18501952
}
18511953

18521954
// extractInputVPackets extracts the vPackets from the inputs passed in. If
18531955
// none of the inputs have any resolution blobs. Then an empty slice will be
18541956
// returned.
1855-
func extractInputVPackets(inputs []input.Input) lfn.Result[[]*tappsbt.VPacket] {
1856-
type returnType = []*tappsbt.VPacket
1957+
func extractInputVPackets(inputs []input.Input) lfn.Result[sweepVpkts] {
1958+
type returnType = sweepVpkts
18571959

18581960
// Otherwise, we'll extract the set of resolution blobs from the inputs
18591961
// passed in.
18601962
relevantInputs := fn.Filter(inputs, func(i input.Input) bool {
18611963
return i.ResolutionBlob().IsSome()
18621964
})
1863-
resolutionBlobs := fn.Map(relevantInputs, func(i input.Input) tlv.Blob {
1864-
// We already know this has a blob from the filter above.
1865-
return i.ResolutionBlob().UnwrapOr(nil)
1866-
})
1965+
resolutionInfo := fn.Map(
1966+
relevantInputs,
1967+
func(i input.Input) lfn.Result[blobWithWitnessInfo] {
1968+
return newBlobWithWitnessInfo(i)
1969+
},
1970+
)
18671971

1868-
// With our set of resolution inputs extracted, we'll now decode them
1869-
// in the vPackets we'll use to generate the output to addr.
1870-
vPkts, err := fn.FlatMapErr(
1871-
resolutionBlobs,
1872-
func(b tlv.Blob) ([]*tappsbt.VPacket, error) {
1873-
var res cmsg.ContractResolution
1874-
if err := res.Decode(bytes.NewReader(b)); err != nil {
1875-
return nil, err
1876-
}
1972+
firstLevelSweeps := lfn.Filter(
1973+
func(info lfn.Result[blobWithWitnessInfo]) bool {
1974+
var secondLevel bool
1975+
info.WhenResult(func(i blobWithWitnessInfo) {
1976+
secondLevel = i.secondLevel
1977+
})
18771978

1878-
return res.VPkts(), nil
1979+
return !secondLevel
18791980
},
1981+
resolutionInfo,
18801982
)
1881-
if err != nil {
1882-
return lfn.Err[returnType](err)
1983+
secondLevelSweeps := lfn.Filter(
1984+
func(info lfn.Result[blobWithWitnessInfo]) bool {
1985+
var secondLevel bool
1986+
info.WhenResult(func(i blobWithWitnessInfo) {
1987+
secondLevel = i.secondLevel
1988+
})
1989+
1990+
return secondLevel
1991+
},
1992+
resolutionInfo,
1993+
)
1994+
1995+
// With our set of resolution inputs extracted, we'll now decode them in
1996+
// the vPackets we'll use to generate the output to addr.
1997+
var vPkts1 []vPktsWithInput
1998+
for _, bRes := range firstLevelSweeps {
1999+
vpkt, err := prepVpkts(bRes, false)
2000+
if err != nil {
2001+
return lfn.Err[sweepVpkts](err)
2002+
}
2003+
2004+
vPkts1 = append(vPkts1, *vpkt)
2005+
}
2006+
2007+
var vPkts2 []vPktsWithInput
2008+
for _, bRes := range secondLevelSweeps {
2009+
vpkt, err := prepVpkts(bRes, true)
2010+
if err != nil {
2011+
return lfn.Err[sweepVpkts](err)
2012+
}
2013+
2014+
vPkts2 = append(vPkts1, *vpkt)
18832015
}
18842016

1885-
return lfn.Ok(vPkts)
2017+
return lfn.Ok(sweepVpkts{
2018+
firstLevel: vPkts1,
2019+
secondLevel: vPkts2,
2020+
})
18862021
}
18872022

18882023
// sweepContracts takes a set of inputs, and the change address we'd use to
@@ -1906,13 +2041,24 @@ func (a *AuxSweeper) sweepContracts(inputs []input.Input,
19062041

19072042
// Now that we know we have a relevant input set, extract all the
19082043
// vPackets from the inputs.
1909-
vPkts, err := extractInputVPackets(inputs).Unpack()
2044+
sPkts, err := extractInputVPackets(inputs).Unpack()
19102045
if err != nil {
19112046
return lfn.Err[returnType](err)
19122047
}
19132048

19142049
log.Infof("Generating anchor output for vpkts=%v",
1915-
limitSpewer.Sdump(vPkts))
2050+
limitSpewer.Sdump(sPkts))
2051+
2052+
// Second level packets will already be anchored to the output assigned
2053+
// to it, so we only need to re-create the commitment for the first
2054+
// level outputs, which can be swept directly into the wallet.
2055+
firstLevelVpkts := sPkts.firstLevelPkts()
2056+
2057+
// If there're no first level vPkts, then we can just return a nil error
2058+
// as we don't have a real sweep output to create.
2059+
if len(firstLevelVpkts) == 0 {
2060+
return lfn.Err[sweep.SweepOutput](nil)
2061+
}
19162062

19172063
// At this point, now that we're about to generate a new output, we'll
19182064
// need an internal key, so we can update all the vPkts.
@@ -1925,8 +2071,8 @@ func (a *AuxSweeper) sweepContracts(inputs []input.Input,
19252071
if err != nil {
19262072
return lfn.Err[returnType](err)
19272073
}
1928-
for idx := range vPkts {
1929-
for _, vOut := range vPkts[idx].Outputs {
2074+
for idx := range firstLevelVpkts {
2075+
for _, vOut := range firstLevelVpkts[idx].Outputs {
19302076
vOut.SetAnchorInternalKey(
19312077
internalKey, a.cfg.ChainParams.HDCoinType,
19322078
)
@@ -1935,7 +2081,7 @@ func (a *AuxSweeper) sweepContracts(inputs []input.Input,
19352081

19362082
// Now that we have our set of resolutions, we'll make a new commitment
19372083
// out of all the vPackets contained.
1938-
outCommitments, err := tapsend.CreateOutputCommitments(vPkts)
2084+
outCommitments, err := tapsend.CreateOutputCommitments(firstLevelVpkts)
19392085
if err != nil {
19402086
return lfn.Errf[returnType]("unable to create "+
19412087
"output commitments: %w", err)

0 commit comments

Comments
 (0)