Skip to content

Commit 57ee008

Browse files
committed
tapchannel: apply tweaks when sweeping HTLCs
1 parent 6a09d43 commit 57ee008

File tree

1 file changed

+91
-29
lines changed

1 file changed

+91
-29
lines changed

tapchannel/aux_sweeper.go

Lines changed: 91 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -726,9 +726,14 @@ func commitRevokeSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
726726

727727
// remoteHtlcTimeoutSweepDesc creates a sweep desc for an HTLC output that is
728728
// close to timing out on the remote party's commitment transaction.
729-
func remoteHtlcTimeoutSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
729+
func remoteHtlcTimeoutSweepDesc(originalKeyRing *lnwallet.CommitmentKeyRing,
730730
payHash []byte, csvDelay uint32, htlcExpiry uint32,
731-
) lfn.Result[tapscriptSweepDescs] {
731+
index input.HtlcIndex) lfn.Result[tapscriptSweepDescs] {
732+
733+
// We're sweeping an HTLC output, which has a tweaked script key. To be
734+
// able to create the correct control block, we need to tweak the key
735+
// ring with the index of the HTLC.
736+
keyRing := TweakedKeyRing(originalKeyRing, index)
732737

733738
// We're sweeping a timed out HTLC, which means that we'll need to
734739
// create the receiver's HTLC script tree (from the remote party's PoV).
@@ -769,8 +774,14 @@ func remoteHtlcTimeoutSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
769774
// remoteHtlcSuccessSweepDesc creates a sweep desc for an HTLC output present on
770775
// the remote party's commitment transaction that we can sweep with the
771776
// preimage.
772-
func remoteHtlcSuccessSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
773-
payHash []byte, csvDelay uint32) lfn.Result[tapscriptSweepDescs] {
777+
func remoteHtlcSuccessSweepDesc(originalKeyRing *lnwallet.CommitmentKeyRing,
778+
payHash []byte, csvDelay uint32,
779+
index input.HtlcIndex) lfn.Result[tapscriptSweepDescs] {
780+
781+
// We're sweeping an HTLC output, which has a tweaked script key. To be
782+
// able to create the correct control block, we need to tweak the key
783+
// ring with the index of the HTLC.
784+
keyRing := TweakedKeyRing(originalKeyRing, index)
774785

775786
// We're planning on sweeping an HTLC that we know the preimage to,
776787
// which the remote party sent, so we'll construct the sender version of
@@ -810,7 +821,7 @@ func remoteHtlcSuccessSweepDesc(keyRing *lnwallet.CommitmentKeyRing,
810821
// present on our local commitment transaction. These are second level HTLCs, so
811822
// we'll need to perform two stages of sweeps.
812823
func localHtlcTimeoutSweepDesc(req lnwallet.ResolutionReq,
813-
) lfn.Result[tapscriptSweepDescs] {
824+
index input.HtlcIndex) lfn.Result[tapscriptSweepDescs] {
814825

815826
isIncoming := false
816827

@@ -827,11 +838,29 @@ func localHtlcTimeoutSweepDesc(req lnwallet.ResolutionReq,
827838
return lfn.Err[tapscriptSweepDescs](err)
828839
}
829840

841+
// We're sweeping an HTLC output, which has a tweaked script key. To be
842+
// able to create the correct control block, we need to tweak the key
843+
// ring with the index of the HTLC.
844+
keyRing := TweakedKeyRing(req.KeyRing, index)
845+
846+
// We also need to overwrite the single tweak in the sign desc with the
847+
// tweak for this HTLC.
848+
sigDesc, err := req.AuxSigDesc.UnwrapOrErr(
849+
fmt.Errorf("no aux sig desc for local HTLC success"),
850+
)
851+
if err != nil {
852+
return lfn.Err[tapscriptSweepDescs](err)
853+
}
854+
sigDesc.SignDetails.SignDesc.SingleTweak = AddTweakWithIndex(
855+
sigDesc.SignDetails.SignDesc.SingleTweak, index,
856+
)
857+
req.AuxSigDesc = lfn.Some(sigDesc)
858+
830859
// We'll need to complete the control block to spend the second-level
831860
// HTLC, so first we'll make the script tree for the HTLC.
832861
htlcScriptTree, err := lnwallet.GenTaprootHtlcScript(
833-
isIncoming, lntypes.Local, htlcExpiry,
834-
payHash, req.KeyRing, lfn.None[txscript.TapLeaf](),
862+
isIncoming, lntypes.Local, htlcExpiry, payHash, keyRing,
863+
lfn.None[txscript.TapLeaf](),
835864
)
836865
if err != nil {
837866
return lfn.Errf[tapscriptSweepDescs]("error creating "+
@@ -862,8 +891,8 @@ func localHtlcTimeoutSweepDesc(req lnwallet.ResolutionReq,
862891
// As this is an HTLC on our local commitment transaction, we'll also
863892
// need to generate a sweep desc for second level HTLC.
864893
secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree(
865-
req.KeyRing.RevocationKey, req.KeyRing.ToLocalKey,
866-
req.CommitCsvDelay, lfn.None[txscript.TapLeaf](),
894+
keyRing.RevocationKey, keyRing.ToLocalKey, req.CommitCsvDelay,
895+
lfn.None[txscript.TapLeaf](),
867896
)
868897
if err != nil {
869898
return lfn.Errf[tapscriptSweepDescs]("error "+
@@ -903,7 +932,7 @@ func localHtlcTimeoutSweepDesc(req lnwallet.ResolutionReq,
903932
// present on our local commitment transaction that we can sweep with a
904933
// preimage. These sweeps take two stages, so we'll add that extra information.
905934
func localHtlcSucessSweepDesc(req lnwallet.ResolutionReq,
906-
) lfn.Result[tapscriptSweepDescs] {
935+
index input.HtlcIndex) lfn.Result[tapscriptSweepDescs] {
907936

908937
isIncoming := true
909938

@@ -920,11 +949,29 @@ func localHtlcSucessSweepDesc(req lnwallet.ResolutionReq,
920949
return lfn.Err[tapscriptSweepDescs](err)
921950
}
922951

952+
// We're sweeping an HTLC output, which has a tweaked script key. To be
953+
// able to create the correct control block, we need to tweak the key
954+
// ring with the index of the HTLC.
955+
keyRing := TweakedKeyRing(req.KeyRing, index)
956+
957+
// We also need to overwrite the single tweak in the sign desc with the
958+
// tweak for this HTLC.
959+
sigDesc, err := req.AuxSigDesc.UnwrapOrErr(
960+
fmt.Errorf("no aux sig desc for local HTLC success"),
961+
)
962+
if err != nil {
963+
return lfn.Err[tapscriptSweepDescs](err)
964+
}
965+
sigDesc.SignDetails.SignDesc.SingleTweak = AddTweakWithIndex(
966+
sigDesc.SignDetails.SignDesc.SingleTweak, index,
967+
)
968+
req.AuxSigDesc = lfn.Some(sigDesc)
969+
923970
// We'll need to complete the control block to spend the second-level
924971
// HTLC, so first we'll make the script tree for the HTLC.
925972
htlcScriptTree, err := lnwallet.GenTaprootHtlcScript(
926973
isIncoming, lntypes.Local, htlcExpiry,
927-
payHash, req.KeyRing, lfn.None[txscript.TapLeaf](),
974+
payHash, keyRing, lfn.None[txscript.TapLeaf](),
928975
)
929976
if err != nil {
930977
return lfn.Errf[tapscriptSweepDescs]("error creating "+
@@ -959,8 +1006,8 @@ func localHtlcSucessSweepDesc(req lnwallet.ResolutionReq,
9591006
// As this is an HTLC on our local commitment transaction, we'll also
9601007
// need to generate a sweep desc for second level HTLC.
9611008
secondLevelScriptTree, err := input.TaprootSecondLevelScriptTree(
962-
req.KeyRing.RevocationKey, req.KeyRing.ToLocalKey,
963-
req.CommitCsvDelay, lfn.None[txscript.TapLeaf](),
1009+
keyRing.RevocationKey, keyRing.ToLocalKey, req.CommitCsvDelay,
1010+
lfn.None[txscript.TapLeaf](),
9641011
)
9651012
if err != nil {
9661013
return lfn.Errf[tapscriptSweepDescs]("error "+
@@ -1706,10 +1753,9 @@ func (a *AuxSweeper) resolveContract(
17061753
// assets for the remote party, which are actually the HTLCs we
17071754
// sent outgoing. We only care about this particular HTLC, so
17081755
// we'll filter out the rest.
1756+
htlcID := req.HtlcID.UnwrapOr(math.MaxUint64)
17091757
htlcOutputs := commitState.OutgoingHtlcAssets.Val
1710-
assetOutputs = htlcOutputs.FilterByHtlcIndex(
1711-
req.HtlcID.UnwrapOr(math.MaxUint64),
1712-
)
1758+
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
17131759

17141760
payHash, err := req.PayHash.UnwrapOrErr(errNoPayHash)
17151761
if err != nil {
@@ -1720,7 +1766,7 @@ func (a *AuxSweeper) resolveContract(
17201766
// sweep desc for the timeout txn.
17211767
sweepDesc = remoteHtlcTimeoutSweepDesc(
17221768
req.KeyRing, payHash[:], req.CsvDelay,
1723-
req.CltvDelay.UnwrapOr(0),
1769+
req.CltvDelay.UnwrapOr(0), htlcID,
17241770
)
17251771

17261772
// The remote party broadcasted a commitment transaction which held an
@@ -1729,10 +1775,9 @@ func (a *AuxSweeper) resolveContract(
17291775
// In this case, it's an outgoing HTLC from the PoV of the
17301776
// remote party, which is incoming for us. We'll only sweep this
17311777
// HTLC, so we'll filter out the rest.
1778+
htlcID := req.HtlcID.UnwrapOr(math.MaxUint64)
17321779
htlcOutputs := commitState.IncomingHtlcAssets.Val
1733-
assetOutputs = htlcOutputs.FilterByHtlcIndex(
1734-
req.HtlcID.UnwrapOr(math.MaxUint64),
1735-
)
1780+
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
17361781

17371782
payHash, err := req.PayHash.UnwrapOrErr(errNoPayHash)
17381783
if err != nil {
@@ -1742,7 +1787,7 @@ func (a *AuxSweeper) resolveContract(
17421787
// Now that we know which output we'll be sweeping, we'll make a
17431788
// sweep desc for the timeout txn.
17441789
sweepDesc = remoteHtlcSuccessSweepDesc(
1745-
req.KeyRing, payHash[:], req.CsvDelay,
1790+
req.KeyRing, payHash[:], req.CsvDelay, htlcID,
17461791
)
17471792

17481793
// In this case, we broadcast a commitment transaction which held an
@@ -1752,14 +1797,13 @@ func (a *AuxSweeper) resolveContract(
17521797
case input.TaprootHtlcLocalOfferedTimeout:
17531798
// Like the other HTLC cases, there's only a single output we
17541799
// care about here.
1800+
htlcID := req.HtlcID.UnwrapOr(math.MaxUint64)
17551801
htlcOutputs := commitState.OutgoingHtlcAssets.Val
1756-
assetOutputs = htlcOutputs.FilterByHtlcIndex(
1757-
req.HtlcID.UnwrapOr(math.MaxUint64),
1758-
)
1802+
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
17591803

17601804
// With the output and pay desc located, we'll now create the
17611805
// sweep desc.
1762-
sweepDesc = localHtlcTimeoutSweepDesc(req)
1806+
sweepDesc = localHtlcTimeoutSweepDesc(req, htlcID)
17631807

17641808
needsSecondLevel = true
17651809

@@ -1768,14 +1812,13 @@ func (a *AuxSweeper) resolveContract(
17681812
// needed to sweep both this output, as well as the second level
17691813
// output it creates.
17701814
case input.TaprootHtlcAcceptedLocalSuccess:
1815+
htlcID := req.HtlcID.UnwrapOr(math.MaxUint64)
17711816
htlcOutputs := commitState.IncomingHtlcAssets.Val
1772-
assetOutputs = htlcOutputs.FilterByHtlcIndex(
1773-
req.HtlcID.UnwrapOr(math.MaxUint64),
1774-
)
1817+
assetOutputs = htlcOutputs.FilterByHtlcIndex(htlcID)
17751818

17761819
// With the output and pay desc located, we'll now create the
17771820
// sweep desc.
1778-
sweepDesc = localHtlcSucessSweepDesc(req)
1821+
sweepDesc = localHtlcSucessSweepDesc(req, htlcID)
17791822

17801823
needsSecondLevel = true
17811824

@@ -2573,3 +2616,22 @@ func (a *AuxSweeper) NotifyBroadcast(req *sweep.BumpRequest,
25732616

25742617
return resp
25752618
}
2619+
2620+
// TweakedKeyRing returns a new commitment key ring with the revocation key
2621+
// tweaked by the given HTLC index.
2622+
func TweakedKeyRing(keyRing *lnwallet.CommitmentKeyRing,
2623+
index input.HtlcIndex) *lnwallet.CommitmentKeyRing {
2624+
2625+
return &lnwallet.CommitmentKeyRing{
2626+
CommitPoint: keyRing.CommitPoint,
2627+
LocalCommitKeyTweak: keyRing.LocalCommitKeyTweak,
2628+
LocalHtlcKeyTweak: keyRing.LocalHtlcKeyTweak,
2629+
LocalHtlcKey: keyRing.LocalHtlcKey,
2630+
RemoteHtlcKey: keyRing.RemoteHtlcKey,
2631+
ToLocalKey: keyRing.ToLocalKey,
2632+
ToRemoteKey: keyRing.ToRemoteKey,
2633+
RevocationKey: TweakPubKeyWithIndex(
2634+
keyRing.RevocationKey, index,
2635+
),
2636+
}
2637+
}

0 commit comments

Comments
 (0)