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
2 changed files with 91 additions and 29 deletions

View File

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

View File

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