contractcourt: update UTXO nursery to support taproot chans

This commit is contained in:
Olaoluwa Osuntokun 2023-03-01 22:17:46 -08:00
parent cdcde6e0a5
commit 7e2d04a310
No known key found for this signature in database
GPG key ID: 3BBD59E99B280306
2 changed files with 94 additions and 13 deletions

View file

@ -10,6 +10,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"
@ -368,10 +369,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.TaprootHtlcAcceptedSuccessSecondLevel
} else {
witType = input.HtlcAcceptedSuccessSecondLevel
}
htlcOutput := makeKidOutput(
&htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
input.HtlcAcceptedSuccessSecondLevel,
&htlcRes.SweepSignDesc, 0,
witType, &htlcRes.SweepSignDesc, 0,
)
if htlcOutput.Amount() > 0 {
@ -396,6 +410,20 @@ 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
@ -403,8 +431,7 @@ func (u *UtxoNursery) IncubateOutputs(chanPoint wire.OutPoint,
// accordingly.
htlcOutput := makeKidOutput(
&htlcRes.ClaimOutpoint, &chanPoint, htlcRes.CsvDelay,
input.HtlcOfferedRemoteTimeout,
&htlcRes.SweepSignDesc, htlcRes.Expiry,
witType, &htlcRes.SweepSignDesc, htlcRes.Expiry,
)
kidOutputs = append(kidOutputs, htlcOutput)
}
@ -516,13 +543,15 @@ func (u *UtxoNursery) NurseryReport(
// confirmation of the commitment transaction.
switch kid.WitnessType() {
case input.HtlcAcceptedSuccessSecondLevel:
case input.HtlcAcceptedSuccessSecondLevel,
input.TaprootHtlcAcceptedSuccessSecondLevel:
// 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
@ -537,7 +566,8 @@ 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
@ -545,6 +575,10 @@ func (u *UtxoNursery) NurseryReport(
// it.
report.AddLimboDirectHtlc(&kid)
case input.TaprootHtlcAcceptedSuccessSecondLevel:
fallthrough
case input.TaprootHtlcOfferedTimeoutSecondLevel:
fallthrough
case input.HtlcAcceptedSuccessSecondLevel:
fallthrough
case input.HtlcOfferedTimeoutSecondLevel:
@ -561,10 +595,16 @@ func (u *UtxoNursery) NurseryReport(
// balance.
switch kid.WitnessType() {
case input.TaprootHtlcAcceptedSuccessSecondLevel:
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
@ -731,7 +771,8 @@ func (u *UtxoNursery) graduateClass(classHeight uint32) error {
// Fetch all information about the crib and kindergarten outputs at
// this height.
kgtnOutputs, cribOutputs, err := u.cfg.Store.FetchClass(
classHeight)
classHeight,
)
if err != nil {
return err
}
@ -1225,7 +1266,17 @@ func makeBabyOutput(chanPoint *wire.OutPoint,
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,
@ -1313,6 +1364,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.TaprootHtlcAcceptedSuccessSecondLevel ||
witnessType == input.TaprootHtlcOfferedRemoteTimeout ||
witnessType == input.HtlcOfferedRemoteTimeout)
// heightHint can be safely set to zero here, because after this
@ -1389,7 +1442,17 @@ func (k *kidOutput) Encode(w io.Writer) error {
return err
}
return input.WriteSignDescriptor(w, k.SignDesc())
if err := input.WriteSignDescriptor(w, k.SignDesc()); err != nil {
return err
}
if k.SignDesc().ControlBlock == nil {
return nil
}
// If this is a taproot output, then it'll also have a control block,
// so we'll go ahead and write that now.
return wire.WriteVarBytes(w, 1000, k.SignDesc().ControlBlock)
}
// Decode takes a byte array representation of a kidOutput and converts it to an
@ -1436,7 +1499,27 @@ func (k *kidOutput) Decode(r io.Reader) error {
}
k.witnessType = input.StandardWitnessType(byteOrder.Uint16(scratch[:2]))
return input.ReadSignDescriptor(r, &k.signDesc)
if err := input.ReadSignDescriptor(r, &k.signDesc); err != nil {
return err
}
// If there's anything left in the reader, then this is a taproot
// output that also wrote a control block.
ctrlBlock, err := wire.ReadVarBytes(r, 0, 1000, "control block")
switch {
// If there're no bytes remaining, then we'll return early.
case errors.Is(err, io.EOF):
fallthrough
case errors.Is(err, io.ErrUnexpectedEOF):
return nil
case err != nil:
return err
}
k.signDesc.ControlBlock = ctrlBlock
return nil
}
// TODO(bvu): copied from channeldb, remove repetition

View file

@ -225,8 +225,6 @@ func WriteSignDescriptor(w io.Writer, sd *SignDescriptor) error {
return err
}
// TODO(roasbeef): also write ctrl block?
return nil
}