contractcourt: update htlcSuccessResolver for taproot chans

This commit is contained in:
Olaoluwa Osuntokun 2023-03-01 22:16:42 -08:00
parent 23f7ee39c7
commit df2a2d83ea
No known key found for this signature in database
GPG key ID: 3BBD59E99B280306
2 changed files with 91 additions and 29 deletions

View file

@ -8,6 +8,7 @@ import (
"io"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/txscript"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/htlcswitch/hop"
@ -103,9 +104,9 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
// If we've locked in an htlc with an invalid payload on our
// commitment tx, we don't need to resolve it. The other party
// will time it out and get their funds back. This situation can
// present itself when we crash before processRemoteAdds in the
// link has ran.
// will time it out and get their funds back. This situation
// can present itself when we crash before processRemoteAdds in
// the link has ran.
h.resolved = true
if err := h.processFinalHtlcFail(); err != nil {
@ -192,17 +193,41 @@ func (h *htlcIncomingContestResolver) Resolve() (ContractResolver, error) {
log.Infof("%T(%v): applied preimage=%v", h,
h.htlcResolution.ClaimOutpoint, preimage)
isSecondLevel := h.htlcResolution.SignedSuccessTx != nil
// If we didn't have to go to the second level to claim (this
// is the remote commitment transaction), then we don't need to
// modify our canned witness.
if !isSecondLevel {
return nil
}
isTaproot := txscript.IsPayToTaproot(
h.htlcResolution.SignedSuccessTx.TxOut[0].PkScript,
)
// If this is our commitment transaction, then we'll need to
// populate the witness for the second-level HTLC transaction.
if h.htlcResolution.SignedSuccessTx != nil {
// Within the witness for the success transaction, the
// preimage is the 4th element as it looks like:
//
// * <sender sig> <recvr sig> <preimage> <witness script>
//
// We'll populate it within the witness, as since this
// was a "contest" resolver, we didn't yet know of the
// preimage.
switch {
// For taproot channels, the witness for sweeping with sucess
// looks like:
// - <sender sig> <receiver sig> <preimage> <success_script>
// <control_block>
//
// So we'll insert it at the 3rd index of the witness.
case isTaproot:
//nolint:lll
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[2] = preimage[:]
// Within the witness for the success transaction, the
// preimage is the 4th element as it looks like:
//
// * <0> <sender sig> <recvr sig> <preimage> <witness script>
//
// We'll populate it within the witness, as since this
// was a "contest" resolver, we didn't yet know of the
// preimage.
case !isTaproot:
h.htlcResolution.SignedSuccessTx.TxIn[0].Witness[3] = preimage[:]
}

View file

@ -7,6 +7,7 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/chainntnfs"
@ -14,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/labels"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/sweep"
)
@ -171,9 +173,9 @@ func (h *htlcSuccessResolver) broadcastSuccessTx() (*wire.OutPoint, error) {
// If we have non-nil SignDetails, this means that have a 2nd level
// HTLC transaction that is signed using sighash SINGLE|ANYONECANPAY
// (the case for anchor type channels). In this case we can re-sign it
// and attach fees at will. We let the sweeper handle this job.
// We use the checkpointed outputIncubating field to determine if we
// already swept the HTLC output into the second level transaction.
// and attach fees at will. We let the sweeper handle this job. We use
// the checkpointed outputIncubating field to determine if we already
// swept the HTLC output into the second level transaction.
if h.htlcResolution.SignDetails != nil {
return h.broadcastReSignedSuccessTx()
}
@ -233,16 +235,29 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (
// We will have to let the sweeper re-sign the success tx and wait for
// it to confirm, if we haven't already.
isTaproot := txscript.IsPayToTaproot(
h.htlcResolution.SweepSignDesc.Output.PkScript,
)
if !h.outputIncubating {
log.Infof("%T(%x): offering second-layer transition tx to "+
"sweeper: %v", h, h.htlc.RHash[:],
spew.Sdump(h.htlcResolution.SignedSuccessTx))
secondLevelInput := input.MakeHtlcSecondLevelSuccessAnchorInput(
h.htlcResolution.SignedSuccessTx,
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
h.broadcastHeight,
)
var secondLevelInput input.HtlcSecondLevelAnchorInput
if isTaproot {
secondLevelInput = input.MakeHtlcSecondLevelSuccessTaprootInput(
h.htlcResolution.SignedSuccessTx,
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
h.broadcastHeight,
)
} else {
secondLevelInput = input.MakeHtlcSecondLevelSuccessAnchorInput(
h.htlcResolution.SignedSuccessTx,
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
h.broadcastHeight,
)
}
_, err := h.Sweeper.SweepInput(
&secondLevelInput,
sweep.Params{
@ -341,13 +356,20 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (
// Let the sweeper sweep the second-level output now that the
// CSV/CLTV locks have expired.
var witType input.StandardWitnessType
if isTaproot {
witType = input.TaprootHtlcAcceptedSuccessSecondLevel
} else {
witType = input.HtlcAcceptedSuccessSecondLevel
}
inp := h.makeSweepInput(
op, input.HtlcAcceptedSuccessSecondLevel,
op, witType,
input.LeaseHtlcAcceptedSuccessSecondLevel,
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.CsvDelay, h.broadcastHeight,
h.htlc.RHash,
)
// TODO(roasbeef): need to update above for leased types
_, err = h.Sweeper.SweepInput(
inp,
sweep.Params{
@ -377,17 +399,32 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
log.Infof("%T(%x): crafting sweep tx for incoming+remote "+
"htlc confirmed", h, h.htlc.RHash[:])
isTaproot := txscript.IsPayToTaproot(
h.htlcResolution.SweepSignDesc.Output.PkScript,
)
// Before we can craft out sweeping transaction, we need to
// create an input which contains all the items required to add
// this input to a sweeping transaction, and generate a
// witness.
inp := input.MakeHtlcSucceedInput(
&h.htlcResolution.ClaimOutpoint,
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.Preimage[:],
h.broadcastHeight,
h.htlcResolution.CsvDelay,
)
var inp input.Input
if isTaproot {
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
&h.htlcResolution.ClaimOutpoint,
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.Preimage[:],
h.broadcastHeight,
h.htlcResolution.CsvDelay,
))
} else {
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
&h.htlcResolution.ClaimOutpoint,
&h.htlcResolution.SweepSignDesc,
h.htlcResolution.Preimage[:],
h.broadcastHeight,
h.htlcResolution.CsvDelay,
))
}
// With the input created, we can now generate the full sweep
// transaction, that we'll use to move these coins back into
@ -400,7 +437,7 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
// TODO: Use time-based sweeper and result chan.
var err error
h.sweepTx, err = h.Sweeper.CreateSweepTx(
[]input.Input{&inp},
[]input.Input{inp},
sweep.FeePreference{
ConfTarget: sweepConfTarget,
}, 0,