Skip to content

Commit

Permalink
contractcourt: update UTXO nursery to support taproot chans
Browse files Browse the repository at this point in the history
  • Loading branch information
Roasbeef committed Jul 27, 2023
1 parent db3c106 commit 93cdb3c
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
72 changes: 64 additions & 8 deletions contractcourt/utxonursery.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sync/atomic"

"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
Expand Down Expand Up @@ -367,10 +368,23 @@ func (u *UtxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
// second-layer HTLC output. We effectively skip the baby stage (as the
// timelock is zero), and enter the kid stage.
for _, htlcRes := range incomingHtlcs {
// Based on the input pk script of the sign descriptor, we can
// determine if this is a taproot output or not. This'll
// determine the witness type we try to set below.
isTaproot := txscript.IsPayToTaproot(
htlcRes.SweepSignDesc.Output.PkScript,
)

var witType input.StandardWitnessType
if isTaproot {
witType = input.TaprootHtlcAcceptedSuceessSecondLevel
} else {
witType = input.HtlcAcceptedSuccessSecondLevel
}

htlcOutput := makeKidOutput(
&htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
input.HtlcAcceptedSuccessSecondLevel,
&htlcRes.SweepSignDesc, 0,
witType, &htlcRes.SweepSignDesc, 0,
)

if htlcOutput.Amount() > 0 {
Expand All @@ -395,15 +409,28 @@ func (u *UtxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
continue
}

// Based on the input pk script of the sign descriptor, we can
// determine if this is a taproot output or not. This'll
// determine the witness type we try to set below.
isTaproot := txscript.IsPayToTaproot(
htlcRes.SweepSignDesc.Output.PkScript,
)

var witType input.StandardWitnessType
if isTaproot {
witType = input.TaprootHtlcOfferedRemoteTimeout
} else {
witType = input.HtlcOfferedRemoteTimeout
}

// Otherwise, this is actually a kid output as we can sweep it
// once the commitment transaction confirms, and the absolute
// CLTV lock has expired. We set the CSV delay what the
// resolution encodes, since the sequence number must be set
// accordingly.
htlcOutput := makeKidOutput(
&htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
input.HtlcOfferedRemoteTimeout,
&htlcRes.SweepSignDesc, htlcRes.Expiry,
witType, &htlcRes.SweepSignDesc, htlcRes.Expiry,
)
kidOutputs = append(kidOutputs, htlcOutput)
}
Expand Down Expand Up @@ -515,13 +542,15 @@ func (u *UtxoNursery) NurseryReport(
// confirmation of the commitment transaction.
switch kid.WitnessType() {

case input.HtlcAcceptedSuccessSecondLevel:
case input.HtlcAcceptedSuccessSecondLevel,
input.TaprootHtlcAcceptedSuceessSecondLevel:
// An HTLC output on our commitment transaction
// where the second-layer transaction hasn't
// yet confirmed.
report.AddLimboStage1SuccessHtlc(&kid)

case input.HtlcOfferedRemoteTimeout:
case input.HtlcOfferedRemoteTimeout,
input.TaprootHtlcOfferedRemoteTimeout:
// This is an HTLC output on the
// commitment transaction of the remote
// party. We are waiting for the CLTV
Expand All @@ -536,14 +565,19 @@ func (u *UtxoNursery) NurseryReport(
// types.
switch kid.WitnessType() {

case input.HtlcOfferedRemoteTimeout:
case input.HtlcOfferedRemoteTimeout,
input.TaprootHtlcOfferedRemoteTimeout:
// This is an HTLC output on the
// commitment transaction of the remote
// party. The CLTV timelock has
// expired, and we only need to sweep
// it.
report.AddLimboDirectHtlc(&kid)

case input.TaprootHtlcAcceptedSuceessSecondLevel:
fallthrough
case input.TaprootHtlcOfferedTimeoutSecondLevel:
fallthrough
case input.HtlcAcceptedSuccessSecondLevel:
fallthrough
case input.HtlcOfferedTimeoutSecondLevel:
Expand All @@ -560,10 +594,16 @@ func (u *UtxoNursery) NurseryReport(
// balance.
switch kid.WitnessType() {

case input.TaprootHtlcAcceptedSuceessSecondLevel:
fallthrough
case input.TaprootHtlcOfferedTimeoutSecondLevel:
fallthrough
case input.HtlcAcceptedSuccessSecondLevel:
fallthrough
case input.HtlcOfferedTimeoutSecondLevel:
fallthrough
case input.TaprootHtlcOfferedRemoteTimeout:
fallthrough
case input.HtlcOfferedRemoteTimeout:
// This htlc output successfully
// resides in a p2wkh output belonging
Expand Down Expand Up @@ -1215,9 +1255,22 @@ type babyOutput struct {
func makeBabyOutput(chanPoint *wire.OutPoint,
htlcResolution *lnwallet.OutgoingHtlcResolution) babyOutput {

// TODO(roasbeef): isn't actually needed since all anchor outputs with
// taproot, so bypasses this?

htlcOutpoint := htlcResolution.ClaimOutpoint
blocksToMaturity := htlcResolution.CsvDelay
witnessType := input.HtlcOfferedTimeoutSecondLevel

isTaproot := txscript.IsPayToTaproot(
htlcResolution.SweepSignDesc.Output.PkScript,
)

var witnessType input.StandardWitnessType
if isTaproot {
witnessType = input.TaprootHtlcOfferedTimeoutSecondLevel
} else {
witnessType = input.HtlcOfferedTimeoutSecondLevel
}

kid := makeKidOutput(
&htlcOutpoint, chanPoint, blocksToMaturity, witnessType,
Expand Down Expand Up @@ -1305,6 +1358,8 @@ func makeKidOutput(outpoint, originChanPoint *wire.OutPoint,
// transaction, or is an outgoing HTLC on the commitment transaction of
// the remote peer.
isHtlc := (witnessType == input.HtlcAcceptedSuccessSecondLevel ||
witnessType == input.TaprootHtlcAcceptedSuceessSecondLevel ||
witnessType == input.TaprootHtlcOfferedRemoteTimeout ||
witnessType == input.HtlcOfferedRemoteTimeout)

// heightHint can be safely set to zero here, because after this
Expand Down Expand Up @@ -1381,6 +1436,7 @@ func (k *kidOutput) Encode(w io.Writer) error {
return err
}

// TODO(roasbeef): can write the control block since at the end?
return input.WriteSignDescriptor(w, k.SignDesc())
}

Expand Down
8 changes: 8 additions & 0 deletions lnutils/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package lnutils

// PTr returns the pointer of the given value. This is useful in instances
// where a function returns the value, but a pointer is wanted. Without this,
// then an intermediate variable is needed.
func Ptr[T any](v T) *T {
return &v
}

0 comments on commit 93cdb3c

Please sign in to comment.