Skip to content

Commit 6e03aee

Browse files
committed
tapchannel: properly thread thru lock time for 2nd level HTLCs
In this commit, we fix an existing logic gap related to 2nd level HTLCs. Before if we were signing a timeout for the remote party, neither of us would properly apply the lock time to the vPkt. In this commit, we fix it by first gaining a new `whoseCommit` param in `FetchLeavesFromCommit`. We then use that to decide when to use a non zero CLTV timeout.
1 parent 728d22b commit 6e03aee

File tree

5 files changed

+71
-16
lines changed

5 files changed

+71
-16
lines changed

server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ func (s *Server) FetchLeavesFromCommit(chanState lnwl.AuxChanState,
771771
// The aux leaf creator is fully stateless, and we don't need to wait
772772
// for the server to be started before being able to use it.
773773
return tapchannel.FetchLeavesFromCommit(
774-
s.chainParams, chanState, com, keys,
774+
s.chainParams, chanState, com, keys, whoseCommit,
775775
)
776776
}
777777

tapchannel/aux_leaf_creator.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"github.com/btcsuite/btcd/txscript"
99
"github.com/btcsuite/btcd/wire"
1010
"github.com/lightninglabs/taproot-assets/address"
11+
"github.com/lightninglabs/taproot-assets/fn"
1112
cmsg "github.com/lightninglabs/taproot-assets/tapchannelmsg"
1213
"github.com/lightningnetwork/lnd/channeldb"
1314
lfn "github.com/lightningnetwork/lnd/fn"
1415
"github.com/lightningnetwork/lnd/input"
16+
"github.com/lightningnetwork/lnd/lntypes"
1517
lnwl "github.com/lightningnetwork/lnd/lnwallet"
1618
"github.com/lightningnetwork/lnd/tlv"
1719
)
@@ -78,7 +80,8 @@ func FetchLeavesFromView(chainParams *address.ChainParams,
7880
// to the passed aux blob, and an existing channel commitment.
7981
func FetchLeavesFromCommit(chainParams *address.ChainParams,
8082
chanState lnwl.AuxChanState, com channeldb.ChannelCommitment,
81-
keys lnwl.CommitmentKeyRing) lfn.Result[lnwl.CommitDiffAuxResult] {
83+
keys lnwl.CommitmentKeyRing,
84+
whoseCommit lntypes.ChannelParty) lfn.Result[lnwl.CommitDiffAuxResult] {
8285

8386
type returnType = lnwl.CommitDiffAuxResult
8487

@@ -115,9 +118,17 @@ func FetchLeavesFromCommit(chainParams *address.ChainParams,
115118
continue
116119
}
117120

121+
// If this is an incoming HTLC (to us), but on the
122+
// remote party's commitment transaction, then they'll
123+
// need to go to the second level to time it out.
124+
var cltvTimeout fn.Option[uint32]
125+
if whoseCommit == lntypes.Remote {
126+
cltvTimeout = fn.Some(htlc.RefundTimeout)
127+
}
128+
118129
leaf, err := CreateSecondLevelHtlcTx(
119130
chanState, com.CommitTx, htlc.Amt.ToSatoshis(),
120-
keys, chainParams, htlcOutputs,
131+
keys, chainParams, htlcOutputs, cltvTimeout,
121132
)
122133
if err != nil {
123134
return lfn.Err[returnType](fmt.Errorf("unable "+
@@ -147,9 +158,17 @@ func FetchLeavesFromCommit(chainParams *address.ChainParams,
147158
continue
148159
}
149160

161+
// If this is an outgoing commit on our local
162+
// commitment, then we'll need to go to the second level
163+
// to time out it out.
164+
var cltvTimeout fn.Option[uint32]
165+
if whoseCommit == lntypes.Local {
166+
cltvTimeout = fn.Some(htlc.RefundTimeout)
167+
}
168+
150169
leaf, err := CreateSecondLevelHtlcTx(
151170
chanState, com.CommitTx, htlc.Amt.ToSatoshis(),
152-
keys, chainParams, htlcOutputs,
171+
keys, chainParams, htlcOutputs, cltvTimeout,
153172
)
154173
if err != nil {
155174
return lfn.Err[returnType](fmt.Errorf("unable "+

tapchannel/aux_leaf_signer.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,17 @@ func verifyHtlcSignature(chainParams *address.ChainParams,
358358
keyRing lnwallet.CommitmentKeyRing, sigs []*cmsg.AssetSig,
359359
htlcOutputs []*cmsg.AssetOutput, baseJob lnwallet.BaseAuxJob) error {
360360

361+
// If we're validating a signature for an outgoing HTLC, then it's an
362+
// outgoing HTLC for the remote party, so we'll need to sign it with the
363+
// proper lock time.
364+
var htlcTimeout fn.Option[uint32]
365+
if !baseJob.Incoming {
366+
htlcTimeout = fn.Some(baseJob.HTLC.Timeout)
367+
}
368+
361369
vPackets, err := htlcSecondLevelPacketsFromCommit(
362370
chainParams, chanState, commitTx, baseJob.KeyRing, htlcOutputs,
363-
baseJob,
371+
baseJob, htlcTimeout,
364372
)
365373
if err != nil {
366374
return fmt.Errorf("error generating second level packets: %w",
@@ -494,9 +502,17 @@ func (s *AuxLeafSigner) generateHtlcSignature(chanState lnwallet.AuxChanState,
494502
signDesc input.SignDescriptor,
495503
baseJob lnwallet.BaseAuxJob) (lnwallet.AuxSigJobResp, error) {
496504

505+
// If we're generating a signature for an incoming HTLC, then it's an
506+
// outgoing HTLC for the remote party, so we'll need to sign it with the
507+
// proper lock time.
508+
var htlcTimeout fn.Option[uint32]
509+
if baseJob.Incoming {
510+
htlcTimeout = fn.Some(baseJob.HTLC.Timeout)
511+
}
512+
497513
vPackets, err := htlcSecondLevelPacketsFromCommit(
498514
s.cfg.ChainParams, chanState, commitTx, baseJob.KeyRing,
499-
htlcOutputs, baseJob,
515+
htlcOutputs, baseJob, htlcTimeout,
500516
)
501517
if err != nil {
502518
return lnwallet.AuxSigJobResp{}, fmt.Errorf("error generating "+
@@ -584,11 +600,12 @@ func (s *AuxLeafSigner) generateHtlcSignature(chanState lnwallet.AuxChanState,
584600
func htlcSecondLevelPacketsFromCommit(chainParams *address.ChainParams,
585601
chanState lnwallet.AuxChanState, commitTx *wire.MsgTx,
586602
keyRing lnwallet.CommitmentKeyRing, htlcOutputs []*cmsg.AssetOutput,
587-
baseJob lnwallet.BaseAuxJob) ([]*tappsbt.VPacket, error) {
603+
baseJob lnwallet.BaseAuxJob,
604+
htlcTimeout fn.Option[uint32]) ([]*tappsbt.VPacket, error) {
588605

589606
packets, _, err := CreateSecondLevelHtlcPackets(
590607
chanState, commitTx, baseJob.HTLC.Amount.ToSatoshis(),
591-
keyRing, chainParams, htlcOutputs,
608+
keyRing, chainParams, htlcOutputs, htlcTimeout,
592609
)
593610
if err != nil {
594611
return nil, fmt.Errorf("error creating second level HTLC "+

tapchannel/aux_sweeper.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,15 @@ func (a *AuxSweeper) createSweepVpackets(sweepInputs []*cmsg.AssetOutput,
201201
// the output information locked in, as this was a pre-signed
202202
// transaction.
203203
if sweepDesc.auxSigInfo.IsSome() {
204+
var cltvTimeout fn.Option[uint32]
205+
sweepDesc.absoluteDelay.WhenSome(func(delay uint64) {
206+
cltvTimeout = fn.Some(uint32(delay))
207+
})
208+
204209
alloc, err := createSecondLevelHtlcAllocations(
205210
resReq.ChanType, resReq.Initiator, sweepInputs,
206211
resReq.HtlcAmt, resReq.CommitCsvDelay, *resReq.KeyRing,
207-
fn.Some(resReq.ContractPoint.Index),
212+
fn.Some(resReq.ContractPoint.Index), cltvTimeout,
208213
)
209214
if err != nil {
210215
return lfn.Err[returnType](err)
@@ -1962,6 +1967,7 @@ func newBlobWithWitnessInfo(i input.Input) lfn.Result[blobWithWitnessInfo] {
19621967
}
19631968

19641969
// prepVpkts decodes the set of vPkts, supplementing them as needed to ensure
1970+
19651971
// all inputs can be swept properly.
19661972
func prepVpkts(bRes lfn.Result[blobWithWitnessInfo],
19671973
secondLevel bool) (*vPktsWithInput, error) {

tapchannel/commitment.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,7 @@ func collectOutputs(a *Allocation,
12261226
func createSecondLevelHtlcAllocations(chanType channeldb.ChannelType,
12271227
initiator bool, htlcOutputs []*cmsg.AssetOutput, htlcAmt btcutil.Amount,
12281228
commitCsvDelay uint32, keys lnwallet.CommitmentKeyRing,
1229-
outputIndex fn.Option[uint32],
1229+
outputIndex fn.Option[uint32], htlcTimeout fn.Option[uint32],
12301230
) ([]*Allocation, error) {
12311231

12321232
// TODO(roasbeef): thaw height not implemented for taproot chans rn
@@ -1267,6 +1267,8 @@ func createSecondLevelHtlcAllocations(chanType channeldb.ChannelType,
12671267
SortTaprootKeyBytes: schnorr.SerializePubKey(
12681268
htlcTree.TaprootKey,
12691269
),
1270+
// TODO(roasbeef): don't need it here?
1271+
CLTV: htlcTimeout.UnwrapOr(0),
12701272
}}
12711273

12721274
return allocations, nil
@@ -1277,14 +1279,13 @@ func createSecondLevelHtlcAllocations(chanType channeldb.ChannelType,
12771279
func CreateSecondLevelHtlcPackets(chanState lnwallet.AuxChanState,
12781280
commitTx *wire.MsgTx, htlcAmt btcutil.Amount,
12791281
keys lnwallet.CommitmentKeyRing, chainParams *address.ChainParams,
1280-
htlcOutputs []*cmsg.AssetOutput) ([]*tappsbt.VPacket, []*Allocation,
1281-
error) {
1282+
htlcOutputs []*cmsg.AssetOutput, htlcTimeout fn.Option[uint32],
1283+
) ([]*tappsbt.VPacket, []*Allocation, error) {
12821284

12831285
allocations, err := createSecondLevelHtlcAllocations(
12841286
chanState.ChanType, chanState.IsInitiator,
1285-
htlcOutputs, htlcAmt,
1286-
uint32(chanState.LocalChanCfg.CsvDelay), keys,
1287-
fn.None[uint32](),
1287+
htlcOutputs, htlcAmt, uint32(chanState.LocalChanCfg.CsvDelay),
1288+
keys, fn.None[uint32](), htlcTimeout,
12881289
)
12891290
if err != nil {
12901291
return nil, nil, err
@@ -1303,6 +1304,15 @@ func CreateSecondLevelHtlcPackets(chanState lnwallet.AuxChanState,
13031304
return nil, nil, fmt.Errorf("error distributing coins: %w", err)
13041305
}
13051306

1307+
// If the HTLC timeout was present, then we'll also manually add it as a
1308+
// param to the vOut here, as it's just used for sorting with
1309+
// allocations.
1310+
for _, vPkt := range vPackets {
1311+
for _, o := range vPkt.Outputs {
1312+
o.LockTime = uint64(htlcTimeout.UnwrapOr(0))
1313+
}
1314+
}
1315+
13061316
ctx := context.Background()
13071317
for idx := range vPackets {
13081318
err := tapsend.PrepareOutputAssets(ctx, vPackets[idx])
@@ -1320,12 +1330,14 @@ func CreateSecondLevelHtlcPackets(chanState lnwallet.AuxChanState,
13201330
func CreateSecondLevelHtlcTx(chanState lnwallet.AuxChanState,
13211331
commitTx *wire.MsgTx, htlcAmt btcutil.Amount,
13221332
keys lnwallet.CommitmentKeyRing, chainParams *address.ChainParams,
1323-
htlcOutputs []*cmsg.AssetOutput) (input.AuxTapLeaf, error) {
1333+
htlcOutputs []*cmsg.AssetOutput, htlcTimeout fn.Option[uint32],
1334+
) (input.AuxTapLeaf, error) {
13241335

13251336
none := input.NoneTapLeaf()
13261337

13271338
vPackets, allocations, err := CreateSecondLevelHtlcPackets(
13281339
chanState, commitTx, htlcAmt, keys, chainParams, htlcOutputs,
1340+
htlcTimeout,
13291341
)
13301342
if err != nil {
13311343
return none, fmt.Errorf("error creating second level HTLC "+
@@ -1353,6 +1365,7 @@ func CreateSecondLevelHtlcTx(chanState lnwallet.AuxChanState,
13531365
if err != nil {
13541366
return none, fmt.Errorf("error creating aux leaf: %w", err)
13551367
}
1368+
13561369
return lfn.Some(auxLeaf), nil
13571370
}
13581371

0 commit comments

Comments
 (0)