Skip to content

Commit 1e1c18b

Browse files
committed
tapchannel: create two sets of vPkts for 1st and 2nd level HTLCs
1 parent 24c02af commit 1e1c18b

File tree

1 file changed

+97
-37
lines changed

1 file changed

+97
-37
lines changed

tapchannel/aux_sweeper.go

Lines changed: 97 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,20 @@ type vPktsWithInput struct {
460460
tapSigDesc lfn.Option[cmsg.TapscriptSigDesc]
461461
}
462462

463+
// isPresigned returns true if the vPktsWithInput is presigned. This will be the
464+
// for an HTLC spent directly from our local commitment transaction.
465+
func (v vPktsWithInput) isPresigned() bool {
466+
witType := v.btcInput.WitnessType()
467+
switch witType {
468+
case input.TaprootHtlcAcceptedLocalSuccess:
469+
return true
470+
case input.TaprootHtlcLocalOfferedTimeout:
471+
return true
472+
default:
473+
return false
474+
}
475+
}
476+
463477
// sweepVpkts contains the set of vPkts needed for sweeping an output. Most
464478
// outputs will only have the first level specified. The second level is needed
465479
// for HTLC outputs on our local commitment transaction.
@@ -501,6 +515,27 @@ func (s sweepVpkts) allPkts() []*tappsbt.VPacket {
501515
return append(s.firstLevelPkts(), s.secondLevelPkts()...)
502516
}
503517

518+
// allVpktsWithInput returns a slice of all vPktsWithInput.
519+
func (s sweepVpkts) allVpktsWithInput() []vPktsWithInput {
520+
return append(s.firstLevel, s.secondLevel...)
521+
}
522+
523+
// directSpendPkts returns the slice of all vPkts that are a direct spend from
524+
// the commitment transaction. This excludes vPkts that are the pre-signed 2nd
525+
// level transaction variant.
526+
func (s sweepVpkts) directSpendPkts() []*tappsbt.VPacket {
527+
directSpends := lfn.Filter(func(vi vPktsWithInput) bool {
528+
return !vi.isPresigned()
529+
}, s.allVpktsWithInput())
530+
directPkts := fn.FlatMap(
531+
directSpends, func(v vPktsWithInput) []*tappsbt.VPacket {
532+
return v.vPkts
533+
},
534+
)
535+
536+
return directPkts
537+
}
538+
504539
// createAndSignSweepVpackets creates vPackets that sweep the funds from the
505540
// channel to the wallet, and then signs them as well.
506541
func (a *AuxSweeper) createAndSignSweepVpackets(
@@ -1177,6 +1212,7 @@ func deriveCommitKeys(req lnwallet.ResolutionReq) (*asset.ScriptKey,
11771212
// importCommitScriptKeys imports the script keys for the commitment outputs
11781213
// into the local addr book.
11791214
func (a *AuxSweeper) importCommitScriptKeys(req lnwallet.ResolutionReq) error {
1215+
11801216
// Generate the local and remote script key, so we can properly import
11811217
// into the addr book, like we did above.
11821218
localCommitScriptKey, remoteCommitScriptKey, err := deriveCommitKeys(
@@ -1815,44 +1851,68 @@ func (a *AuxSweeper) resolveContract(
18151851
return lfn.Err[tlv.Blob](err)
18161852
}
18171853

1818-
// We can't yet make the second level proof as we don't know exactly
1819-
// what the final txid of the sweep transaction will be. So we'll use a
1820-
// blank proof in place.
1821-
//
1822-
// TODO(roasbeef): need to set internal key? already known
1823-
var proof proof.Proof
1824-
1825-
// We'll make a place holder for the second level output based on the
1826-
// assetID+value tuple.
1827-
secondLevelInputs := []*cmsg.AssetOutput{cmsg.NewAssetOutput(
1828-
assetOutputs[0].AssetID.Val, assetOutputs[0].Amount.Val, proof,
1829-
)}
1830-
1831-
// Unlike the first level packets, we can't yet sign the second level
1832-
// packets yet, as we don't know what the sweeping transaction will look
1833-
// like. So we'll just create them.
1834-
secondLevelPkts, err := lfn.MapOption(
1835-
func(desc tapscriptSweepDesc) lfn.Result[[]*tappsbt.VPacket] {
1836-
return a.createSweepVpackets(
1837-
secondLevelInputs, lfn.Ok(desc), req,
1838-
)
1839-
},
1840-
)(tapSweepDesc.secondLevel).UnwrapOr(
1841-
lfn.Ok[[]*tappsbt.VPacket](nil),
1842-
).Unpack()
1843-
if err != nil {
1844-
return lfn.Err[tlv.Blob](err)
1845-
}
1854+
var (
1855+
secondLevelPkts []*tappsbt.VPacket
1856+
secondLevelSigDesc lfn.Option[cmsg.TapscriptSigDesc]
1857+
)
18461858

1847-
// With the vPackets fully generated and signed above, we'll serialize
1848-
// it into a resolution blob to return.
1849-
secondLevelSigDesc := lfn.MapOption(
1850-
func(d tapscriptSweepDesc) cmsg.TapscriptSigDesc {
1851-
return cmsg.NewTapscriptSigDesc(
1852-
d.scriptTree.TapTweak(), d.ctrlBlockBytes,
1853-
)
1854-
},
1855-
)(tapSweepDesc.secondLevel)
1859+
// We'll only need a set of second level packets if we're sweeping a set
1860+
// of HTLC outputs on the local party's commitment transaction.
1861+
if needsSecondLevel {
1862+
log.Infof("Creating+signing 2nd level vPkts")
1863+
1864+
// We'll make a place holder for the second level output based
1865+
// on the assetID+value tuple.
1866+
secondLevelInputs := []*cmsg.AssetOutput{cmsg.NewAssetOutput(
1867+
assetOutputs[0].AssetID.Val,
1868+
assetOutputs[0].Amount.Val, assetOutputs[0].Proof.Val,
1869+
)}
1870+
1871+
// Unlike the first level packets, we can't yet sign the second
1872+
// level packets yet, as we don't know what the sweeping
1873+
// transaction will look like. So we'll just create them.
1874+
secondLevelPkts, err = lfn.MapOption(
1875+
func(desc tapscriptSweepDesc) lfn.Result[[]*tappsbt.VPacket] {
1876+
return a.createSweepVpackets(
1877+
secondLevelInputs, lfn.Ok(desc), req,
1878+
)
1879+
},
1880+
)(tapSweepDesc.secondLevel).UnwrapOr(
1881+
lfn.Ok[[]*tappsbt.VPacket](nil),
1882+
).Unpack()
1883+
if err != nil {
1884+
return lfn.Errf[tlv.Blob]("unable to make "+
1885+
"second level pkts: %w", err)
1886+
}
1887+
1888+
// We'll update some of the details of the 2nd level pkt based
1889+
// on the first lvl packet created above (as we don't yet have
1890+
// the full proof for the first lvl packet above).
1891+
for pktIdx, vPkt := range secondLevelPkts {
1892+
prevAsset := firstLevelPkts[pktIdx].Outputs[0].Asset
1893+
1894+
for inputIdx, vIn := range vPkt.Inputs {
1895+
//nolint:lll
1896+
prevScriptKey := prevAsset.ScriptKey
1897+
vIn.PrevID.ScriptKey = asset.ToSerialized(
1898+
prevScriptKey.PubKey,
1899+
)
1900+
1901+
vPkt.SetInputAsset(inputIdx, prevAsset)
1902+
}
1903+
}
1904+
1905+
// With the vPackets fully generated and signed above, we'll
1906+
// serialize it into a resolution blob to return.
1907+
secondLevelSigDesc = lfn.MapOption(
1908+
func(d tapscriptSweepDesc) cmsg.TapscriptSigDesc {
1909+
return cmsg.NewTapscriptSigDesc(
1910+
d.scriptTree.TapTweak(),
1911+
d.ctrlBlockBytes,
1912+
)
1913+
},
1914+
)(tapSweepDesc.secondLevel)
1915+
}
18561916

18571917
res := cmsg.NewContractResolution(
18581918
firstLevelPkts, secondLevelPkts, secondLevelSigDesc,

0 commit comments

Comments
 (0)