mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-09-03 07:43:39 +02:00
contractcourt: add spend path helpers in timeout/success resolver
This commit adds a few helper methods to decide how the htlc output should be spent.
This commit is contained in:
@@ -127,7 +127,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
|||||||
|
|
||||||
// If we don't have a success transaction, then this means that this is
|
// If we don't have a success transaction, then this means that this is
|
||||||
// an output on the remote party's commitment transaction.
|
// an output on the remote party's commitment transaction.
|
||||||
if h.htlcResolution.SignedSuccessTx == nil {
|
if h.isRemoteCommitOutput() {
|
||||||
return h.resolveRemoteCommitOutput()
|
return h.resolveRemoteCommitOutput()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ func (h *htlcSuccessResolver) broadcastSuccessTx() (
|
|||||||
// and attach fees at will. We let the sweeper handle this job. We use
|
// and attach fees at will. We let the sweeper handle this job. We use
|
||||||
// the checkpointed outputIncubating field to determine if we already
|
// the checkpointed outputIncubating field to determine if we 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.isZeroFeeOutput() {
|
||||||
return h.broadcastReSignedSuccessTx()
|
return h.broadcastReSignedSuccessTx()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +236,9 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
|||||||
|
|
||||||
// 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 {
|
||||||
var secondLevelInput input.HtlcSecondLevelAnchorInput
|
var secondLevelInput input.HtlcSecondLevelAnchorInput
|
||||||
if isTaproot {
|
if h.isTaproot() {
|
||||||
//nolint:ll
|
//nolint:ll
|
||||||
secondLevelInput = input.MakeHtlcSecondLevelSuccessTaprootInput(
|
secondLevelInput = input.MakeHtlcSecondLevelSuccessTaprootInput(
|
||||||
h.htlcResolution.SignedSuccessTx,
|
h.htlcResolution.SignedSuccessTx,
|
||||||
@@ -371,7 +368,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
|||||||
// 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
|
var witType input.StandardWitnessType
|
||||||
if isTaproot {
|
if h.isTaproot() {
|
||||||
witType = input.TaprootHtlcAcceptedSuccessSecondLevel
|
witType = input.TaprootHtlcAcceptedSuccessSecondLevel
|
||||||
} else {
|
} else {
|
||||||
witType = input.HtlcAcceptedSuccessSecondLevel
|
witType = input.HtlcAcceptedSuccessSecondLevel
|
||||||
@@ -421,16 +418,12 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
|||||||
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
||||||
ContractResolver, error) {
|
ContractResolver, error) {
|
||||||
|
|
||||||
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.
|
||||||
var inp input.Input
|
var inp input.Input
|
||||||
if isTaproot {
|
if h.isTaproot() {
|
||||||
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
||||||
&h.htlcResolution.ClaimOutpoint,
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
&h.htlcResolution.SweepSignDesc,
|
&h.htlcResolution.SweepSignDesc,
|
||||||
@@ -712,3 +705,29 @@ func (h *htlcSuccessResolver) SupplementDeadline(_ fn.Option[int32]) {
|
|||||||
// A compile time assertion to ensure htlcSuccessResolver meets the
|
// A compile time assertion to ensure htlcSuccessResolver meets the
|
||||||
// ContractResolver interface.
|
// ContractResolver interface.
|
||||||
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
|
var _ htlcContractResolver = (*htlcSuccessResolver)(nil)
|
||||||
|
|
||||||
|
// isRemoteCommitOutput returns a bool to indicate whether the htlc output is
|
||||||
|
// on the remote commitment.
|
||||||
|
func (h *htlcSuccessResolver) isRemoteCommitOutput() bool {
|
||||||
|
// If we don't have a success transaction, then this means that this is
|
||||||
|
// an output on the remote party's commitment transaction.
|
||||||
|
return h.htlcResolution.SignedSuccessTx == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isZeroFeeOutput returns a boolean indicating whether the htlc output is from
|
||||||
|
// a anchor-enabled channel, which uses the sighash SINGLE|ANYONECANPAY.
|
||||||
|
func (h *htlcSuccessResolver) isZeroFeeOutput() bool {
|
||||||
|
// If we have non-nil SignDetails, this means it has 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.
|
||||||
|
return h.htlcResolution.SignedSuccessTx != nil &&
|
||||||
|
h.htlcResolution.SignDetails != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTaproot returns true if the resolver is for a taproot output.
|
||||||
|
func (h *htlcSuccessResolver) isTaproot() bool {
|
||||||
|
return txscript.IsPayToTaproot(
|
||||||
|
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -634,7 +634,7 @@ func (h *htlcTimeoutResolver) spendHtlcOutput() (
|
|||||||
// 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.
|
||||||
case h.htlcResolution.SignDetails != nil && !h.outputIncubating:
|
case h.isZeroFeeOutput() && !h.outputIncubating:
|
||||||
if err := h.sweepSecondLevelTx(); err != nil {
|
if err := h.sweepSecondLevelTx(); err != nil {
|
||||||
log.Errorf("Sending timeout tx to sweeper: %v", err)
|
log.Errorf("Sending timeout tx to sweeper: %v", err)
|
||||||
|
|
||||||
@@ -643,7 +643,7 @@ func (h *htlcTimeoutResolver) spendHtlcOutput() (
|
|||||||
|
|
||||||
// If this is a remote commitment there's no second level timeout txn,
|
// If this is a remote commitment there's no second level timeout txn,
|
||||||
// and we can just send this directly to the sweeper.
|
// and we can just send this directly to the sweeper.
|
||||||
case h.htlcResolution.SignedTimeoutTx == nil && !h.outputIncubating:
|
case h.isRemoteCommitOutput() && !h.outputIncubating:
|
||||||
if err := h.sweepDirectHtlcOutput(); err != nil {
|
if err := h.sweepDirectHtlcOutput(); err != nil {
|
||||||
log.Errorf("Sending direct spend to sweeper: %v", err)
|
log.Errorf("Sending direct spend to sweeper: %v", err)
|
||||||
|
|
||||||
@@ -653,7 +653,7 @@ func (h *htlcTimeoutResolver) spendHtlcOutput() (
|
|||||||
// If we have a SignedTimeoutTx but no SignDetails, this is a local
|
// If we have a SignedTimeoutTx but no SignDetails, this is a local
|
||||||
// commitment for a non-anchor channel, so we'll send it to the utxo
|
// commitment for a non-anchor channel, so we'll send it to the utxo
|
||||||
// nursery.
|
// nursery.
|
||||||
case h.htlcResolution.SignDetails == nil && !h.outputIncubating:
|
case h.isLegacyOutput() && !h.outputIncubating:
|
||||||
if err := h.sendSecondLevelTxLegacy(); err != nil {
|
if err := h.sendSecondLevelTxLegacy(); err != nil {
|
||||||
log.Errorf("Sending timeout tx to nursery: %v", err)
|
log.Errorf("Sending timeout tx to nursery: %v", err)
|
||||||
|
|
||||||
@@ -769,7 +769,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend(
|
|||||||
// If the sweeper is handling the second level transaction, wait for
|
// If the sweeper is handling the second level transaction, wait for
|
||||||
// the CSV and possible CLTV lock to expire, before sweeping the output
|
// the CSV and possible CLTV lock to expire, before sweeping the output
|
||||||
// on the second-level.
|
// on the second-level.
|
||||||
case h.htlcResolution.SignDetails != nil:
|
case h.isZeroFeeOutput():
|
||||||
waitHeight := h.deriveWaitHeight(
|
waitHeight := h.deriveWaitHeight(
|
||||||
h.htlcResolution.CsvDelay, commitSpend,
|
h.htlcResolution.CsvDelay, commitSpend,
|
||||||
)
|
)
|
||||||
@@ -851,7 +851,7 @@ func (h *htlcTimeoutResolver) handleCommitSpend(
|
|||||||
// Finally, if this was an output on our commitment transaction, we'll
|
// Finally, if this was an output on our commitment transaction, we'll
|
||||||
// wait for the second-level HTLC output to be spent, and for that
|
// wait for the second-level HTLC output to be spent, and for that
|
||||||
// transaction itself to confirm.
|
// transaction itself to confirm.
|
||||||
case h.htlcResolution.SignedTimeoutTx != nil:
|
case !h.isRemoteCommitOutput():
|
||||||
log.Infof("%T(%v): waiting for nursery/sweeper to spend CSV "+
|
log.Infof("%T(%v): waiting for nursery/sweeper to spend CSV "+
|
||||||
"delayed output", h, claimOutpoint)
|
"delayed output", h, claimOutpoint)
|
||||||
|
|
||||||
@@ -1232,7 +1232,7 @@ func (h *htlcTimeoutResolver) consumeSpendEvents(resultChan chan *spendResult,
|
|||||||
// continue the loop.
|
// continue the loop.
|
||||||
hasPreimage := isPreimageSpend(
|
hasPreimage := isPreimageSpend(
|
||||||
h.isTaproot(), spendDetail,
|
h.isTaproot(), spendDetail,
|
||||||
h.htlcResolution.SignedTimeoutTx != nil,
|
!h.isRemoteCommitOutput(),
|
||||||
)
|
)
|
||||||
if !hasPreimage {
|
if !hasPreimage {
|
||||||
log.Debugf("HTLC output %s spent doesn't "+
|
log.Debugf("HTLC output %s spent doesn't "+
|
||||||
@@ -1260,3 +1260,31 @@ func (h *htlcTimeoutResolver) consumeSpendEvents(resultChan chan *spendResult,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isRemoteCommitOutput returns a bool to indicate whether the htlc output is
|
||||||
|
// on the remote commitment.
|
||||||
|
func (h *htlcTimeoutResolver) isRemoteCommitOutput() bool {
|
||||||
|
// If we don't have a timeout transaction, then this means that this is
|
||||||
|
// an output on the remote party's commitment transaction.
|
||||||
|
return h.htlcResolution.SignedTimeoutTx == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isZeroFeeOutput returns a boolean indicating whether the htlc output is from
|
||||||
|
// a anchor-enabled channel, which uses the sighash SINGLE|ANYONECANPAY.
|
||||||
|
func (h *htlcTimeoutResolver) isZeroFeeOutput() bool {
|
||||||
|
// If we have non-nil SignDetails, this means it has 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.
|
||||||
|
return h.htlcResolution.SignedTimeoutTx != nil &&
|
||||||
|
h.htlcResolution.SignDetails != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isLegacyOutput returns a boolean indicating whether the htlc output is from
|
||||||
|
// a non-anchor-enabled channel.
|
||||||
|
func (h *htlcTimeoutResolver) isLegacyOutput() bool {
|
||||||
|
// If we have a SignedTimeoutTx but no SignDetails, this is a local
|
||||||
|
// commitment for a non-anchor channel.
|
||||||
|
return h.htlcResolution.SignedTimeoutTx != nil &&
|
||||||
|
h.htlcResolution.SignDetails == nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user