mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-03 18:22:25 +02:00
contractcourt: add sweep senders in htlcSuccessResolver
This commit is a pure refactor in which moves the sweep handling logic into the new methods.
This commit is contained in:
@@ -237,55 +237,7 @@ 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.
|
||||||
if !h.outputIncubating {
|
if !h.outputIncubating {
|
||||||
var secondLevelInput input.HtlcSecondLevelAnchorInput
|
err := h.sweepSuccessTx()
|
||||||
if h.isTaproot() {
|
|
||||||
//nolint:ll
|
|
||||||
secondLevelInput = input.MakeHtlcSecondLevelSuccessTaprootInput(
|
|
||||||
h.htlcResolution.SignedSuccessTx,
|
|
||||||
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
|
||||||
h.broadcastHeight,
|
|
||||||
input.WithResolutionBlob(
|
|
||||||
h.htlcResolution.ResolutionBlob,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
//nolint:ll
|
|
||||||
secondLevelInput = input.MakeHtlcSecondLevelSuccessAnchorInput(
|
|
||||||
h.htlcResolution.SignedSuccessTx,
|
|
||||||
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
|
||||||
h.broadcastHeight,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the budget for this sweep.
|
|
||||||
value := btcutil.Amount(
|
|
||||||
secondLevelInput.SignDesc().Output.Value,
|
|
||||||
)
|
|
||||||
budget := calculateBudget(
|
|
||||||
value, h.Budget.DeadlineHTLCRatio,
|
|
||||||
h.Budget.DeadlineHTLC,
|
|
||||||
)
|
|
||||||
|
|
||||||
// The deadline would be the CLTV in this HTLC output. If we
|
|
||||||
// are the initiator of this force close, with the default
|
|
||||||
// `IncomingBroadcastDelta`, it means we have 10 blocks left
|
|
||||||
// when going onchain. Given we need to mine one block to
|
|
||||||
// confirm the force close tx, and one more block to trigger
|
|
||||||
// the sweep, we have 8 blocks left to sweep the HTLC.
|
|
||||||
deadline := fn.Some(int32(h.htlc.RefundTimeout))
|
|
||||||
|
|
||||||
log.Infof("%T(%x): offering second-level HTLC success tx to "+
|
|
||||||
"sweeper with deadline=%v, budget=%v", h,
|
|
||||||
h.htlc.RHash[:], h.htlc.RefundTimeout, budget)
|
|
||||||
|
|
||||||
// We'll now offer the second-level transaction to the sweeper.
|
|
||||||
_, err := h.Sweeper.SweepInput(
|
|
||||||
&secondLevelInput,
|
|
||||||
sweep.Params{
|
|
||||||
Budget: budget,
|
|
||||||
DeadlineHeight: deadline,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -316,99 +268,18 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
|||||||
"confirmed!", h, h.htlc.RHash[:])
|
"confirmed!", h, h.htlc.RHash[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we ended up here after a restart, we must again get the
|
err := h.sweepSuccessTxOutput()
|
||||||
// spend notification.
|
|
||||||
if commitSpend == nil {
|
|
||||||
var err error
|
|
||||||
commitSpend, err = waitForSpend(
|
|
||||||
&h.htlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint,
|
|
||||||
h.htlcResolution.SignDetails.SignDesc.Output.PkScript,
|
|
||||||
h.broadcastHeight, h.Notifier, h.quit,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The HTLC success tx has a CSV lock that we must wait for, and if
|
|
||||||
// this is a lease enforced channel and we're the imitator, we may need
|
|
||||||
// to wait for longer.
|
|
||||||
waitHeight := h.deriveWaitHeight(
|
|
||||||
h.htlcResolution.CsvDelay, commitSpend,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Now that the sweeper has broadcasted the second-level transaction,
|
|
||||||
// it has confirmed, and we have checkpointed our state, we'll sweep
|
|
||||||
// the second level output. We report the resolver has moved the next
|
|
||||||
// stage.
|
|
||||||
h.reportLock.Lock()
|
|
||||||
h.currentReport.Stage = 2
|
|
||||||
h.currentReport.MaturityHeight = waitHeight
|
|
||||||
h.reportLock.Unlock()
|
|
||||||
|
|
||||||
if h.hasCLTV() {
|
|
||||||
log.Infof("%T(%x): waiting for CSV and CLTV lock to "+
|
|
||||||
"expire at height %v", h, h.htlc.RHash[:],
|
|
||||||
waitHeight)
|
|
||||||
} else {
|
|
||||||
log.Infof("%T(%x): waiting for CSV lock to expire at "+
|
|
||||||
"height %v", h, h.htlc.RHash[:], waitHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll use this input index to determine the second-level output
|
|
||||||
// index on the transaction, as the signatures requires the indexes to
|
|
||||||
// be the same. We don't look for the second-level output script
|
|
||||||
// directly, as there might be more than one HTLC output to the same
|
|
||||||
// pkScript.
|
|
||||||
op := &wire.OutPoint{
|
|
||||||
Hash: *commitSpend.SpenderTxHash,
|
|
||||||
Index: commitSpend.SpenderInputIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the sweeper sweep the second-level output now that the
|
|
||||||
// CSV/CLTV locks have expired.
|
|
||||||
var witType input.StandardWitnessType
|
|
||||||
if h.isTaproot() {
|
|
||||||
witType = input.TaprootHtlcAcceptedSuccessSecondLevel
|
|
||||||
} else {
|
|
||||||
witType = input.HtlcAcceptedSuccessSecondLevel
|
|
||||||
}
|
|
||||||
inp := h.makeSweepInput(
|
|
||||||
op, witType,
|
|
||||||
input.LeaseHtlcAcceptedSuccessSecondLevel,
|
|
||||||
&h.htlcResolution.SweepSignDesc,
|
|
||||||
h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
|
|
||||||
h.htlc.RHash, h.htlcResolution.ResolutionBlob,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Calculate the budget for this sweep.
|
|
||||||
budget := calculateBudget(
|
|
||||||
btcutil.Amount(inp.SignDesc().Output.Value),
|
|
||||||
h.Budget.NoDeadlineHTLCRatio,
|
|
||||||
h.Budget.NoDeadlineHTLC,
|
|
||||||
)
|
|
||||||
|
|
||||||
log.Infof("%T(%x): offering second-level success tx output to sweeper "+
|
|
||||||
"with no deadline and budget=%v at height=%v", h,
|
|
||||||
h.htlc.RHash[:], budget, waitHeight)
|
|
||||||
|
|
||||||
// TODO(roasbeef): need to update above for leased types
|
|
||||||
_, err := h.Sweeper.SweepInput(
|
|
||||||
inp,
|
|
||||||
sweep.Params{
|
|
||||||
Budget: budget,
|
|
||||||
|
|
||||||
// For second level success tx, there's no rush to get
|
|
||||||
// it confirmed, so we use a nil deadline.
|
|
||||||
DeadlineHeight: fn.None[int32](),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will return this outpoint, when this is spent the resolver is fully
|
// Will return this outpoint, when this is spent the resolver is fully
|
||||||
// resolved.
|
// resolved.
|
||||||
|
op := &wire.OutPoint{
|
||||||
|
Hash: *commitSpend.SpenderTxHash,
|
||||||
|
Index: commitSpend.SpenderInputIndex,
|
||||||
|
}
|
||||||
|
|
||||||
return op, nil
|
return op, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,53 +289,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
|||||||
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
||||||
ContractResolver, error) {
|
ContractResolver, error) {
|
||||||
|
|
||||||
// Before we can craft out sweeping transaction, we need to
|
err := h.sweepRemoteCommitOutput()
|
||||||
// create an input which contains all the items required to add
|
|
||||||
// this input to a sweeping transaction, and generate a
|
|
||||||
// witness.
|
|
||||||
var inp input.Input
|
|
||||||
if h.isTaproot() {
|
|
||||||
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
|
||||||
&h.htlcResolution.ClaimOutpoint,
|
|
||||||
&h.htlcResolution.SweepSignDesc,
|
|
||||||
h.htlcResolution.Preimage[:],
|
|
||||||
h.broadcastHeight,
|
|
||||||
h.htlcResolution.CsvDelay,
|
|
||||||
input.WithResolutionBlob(
|
|
||||||
h.htlcResolution.ResolutionBlob,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
|
||||||
&h.htlcResolution.ClaimOutpoint,
|
|
||||||
&h.htlcResolution.SweepSignDesc,
|
|
||||||
h.htlcResolution.Preimage[:],
|
|
||||||
h.broadcastHeight,
|
|
||||||
h.htlcResolution.CsvDelay,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the budget for this sweep.
|
|
||||||
budget := calculateBudget(
|
|
||||||
btcutil.Amount(inp.SignDesc().Output.Value),
|
|
||||||
h.Budget.DeadlineHTLCRatio,
|
|
||||||
h.Budget.DeadlineHTLC,
|
|
||||||
)
|
|
||||||
|
|
||||||
deadline := fn.Some(int32(h.htlc.RefundTimeout))
|
|
||||||
|
|
||||||
log.Infof("%T(%x): offering direct-preimage HTLC output to sweeper "+
|
|
||||||
"with deadline=%v, budget=%v", h, h.htlc.RHash[:],
|
|
||||||
h.htlc.RefundTimeout, budget)
|
|
||||||
|
|
||||||
// We'll now offer the direct preimage HTLC to the sweeper.
|
|
||||||
_, err := h.Sweeper.SweepInput(
|
|
||||||
inp,
|
|
||||||
sweep.Params{
|
|
||||||
Budget: budget,
|
|
||||||
DeadlineHeight: deadline,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -731,3 +556,195 @@ func (h *htlcSuccessResolver) isTaproot() bool {
|
|||||||
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sweepRemoteCommitOutput creates a sweep request to sweep the HTLC output on
|
||||||
|
// the remote commitment via the direct preimage-spend.
|
||||||
|
func (h *htlcSuccessResolver) sweepRemoteCommitOutput() error {
|
||||||
|
// 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.
|
||||||
|
var inp input.Input
|
||||||
|
|
||||||
|
if h.isTaproot() {
|
||||||
|
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
||||||
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
|
&h.htlcResolution.SweepSignDesc,
|
||||||
|
h.htlcResolution.Preimage[:],
|
||||||
|
h.broadcastHeight,
|
||||||
|
h.htlcResolution.CsvDelay,
|
||||||
|
input.WithResolutionBlob(
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
||||||
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
|
&h.htlcResolution.SweepSignDesc,
|
||||||
|
h.htlcResolution.Preimage[:],
|
||||||
|
h.broadcastHeight,
|
||||||
|
h.htlcResolution.CsvDelay,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the budget for this sweep.
|
||||||
|
budget := calculateBudget(
|
||||||
|
btcutil.Amount(inp.SignDesc().Output.Value),
|
||||||
|
h.Budget.DeadlineHTLCRatio,
|
||||||
|
h.Budget.DeadlineHTLC,
|
||||||
|
)
|
||||||
|
|
||||||
|
deadline := fn.Some(int32(h.htlc.RefundTimeout))
|
||||||
|
|
||||||
|
log.Infof("%T(%x): offering direct-preimage HTLC output to sweeper "+
|
||||||
|
"with deadline=%v, budget=%v", h, h.htlc.RHash[:],
|
||||||
|
h.htlc.RefundTimeout, budget)
|
||||||
|
|
||||||
|
// We'll now offer the direct preimage HTLC to the sweeper.
|
||||||
|
_, err := h.Sweeper.SweepInput(
|
||||||
|
inp,
|
||||||
|
sweep.Params{
|
||||||
|
Budget: budget,
|
||||||
|
DeadlineHeight: deadline,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sweepSuccessTx attempts to sweep the second level success tx.
|
||||||
|
func (h *htlcSuccessResolver) sweepSuccessTx() error {
|
||||||
|
var secondLevelInput input.HtlcSecondLevelAnchorInput
|
||||||
|
if h.isTaproot() {
|
||||||
|
secondLevelInput = input.MakeHtlcSecondLevelSuccessTaprootInput(
|
||||||
|
h.htlcResolution.SignedSuccessTx,
|
||||||
|
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
||||||
|
h.broadcastHeight, input.WithResolutionBlob(
|
||||||
|
h.htlcResolution.ResolutionBlob,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
secondLevelInput = input.MakeHtlcSecondLevelSuccessAnchorInput(
|
||||||
|
h.htlcResolution.SignedSuccessTx,
|
||||||
|
h.htlcResolution.SignDetails, h.htlcResolution.Preimage,
|
||||||
|
h.broadcastHeight,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the budget for this sweep.
|
||||||
|
value := btcutil.Amount(secondLevelInput.SignDesc().Output.Value)
|
||||||
|
budget := calculateBudget(
|
||||||
|
value, h.Budget.DeadlineHTLCRatio, h.Budget.DeadlineHTLC,
|
||||||
|
)
|
||||||
|
|
||||||
|
// The deadline would be the CLTV in this HTLC output. If we are the
|
||||||
|
// initiator of this force close, with the default
|
||||||
|
// `IncomingBroadcastDelta`, it means we have 10 blocks left when going
|
||||||
|
// onchain.
|
||||||
|
deadline := fn.Some(int32(h.htlc.RefundTimeout))
|
||||||
|
|
||||||
|
h.log.Infof("offering second-level HTLC success tx to sweeper with "+
|
||||||
|
"deadline=%v, budget=%v", h.htlc.RefundTimeout, budget)
|
||||||
|
|
||||||
|
// We'll now offer the second-level transaction to the sweeper.
|
||||||
|
_, err := h.Sweeper.SweepInput(
|
||||||
|
&secondLevelInput,
|
||||||
|
sweep.Params{
|
||||||
|
Budget: budget,
|
||||||
|
DeadlineHeight: deadline,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sweepSuccessTxOutput attempts to sweep the output of the second level
|
||||||
|
// success tx.
|
||||||
|
func (h *htlcSuccessResolver) sweepSuccessTxOutput() error {
|
||||||
|
h.log.Debugf("sweeping output %v from 2nd-level HTLC success tx",
|
||||||
|
h.htlcResolution.ClaimOutpoint)
|
||||||
|
|
||||||
|
// This should be non-blocking as we will only attempt to sweep the
|
||||||
|
// output when the second level tx has already been confirmed. In other
|
||||||
|
// words, waitForSpend will return immediately.
|
||||||
|
commitSpend, err := waitForSpend(
|
||||||
|
&h.htlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint,
|
||||||
|
h.htlcResolution.SignDetails.SignDesc.Output.PkScript,
|
||||||
|
h.broadcastHeight, h.Notifier, h.quit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HTLC success tx has a CSV lock that we must wait for, and if
|
||||||
|
// this is a lease enforced channel and we're the imitator, we may need
|
||||||
|
// to wait for longer.
|
||||||
|
waitHeight := h.deriveWaitHeight(h.htlcResolution.CsvDelay, commitSpend)
|
||||||
|
|
||||||
|
// Now that the sweeper has broadcasted the second-level transaction,
|
||||||
|
// it has confirmed, and we have checkpointed our state, we'll sweep
|
||||||
|
// the second level output. We report the resolver has moved the next
|
||||||
|
// stage.
|
||||||
|
h.reportLock.Lock()
|
||||||
|
h.currentReport.Stage = 2
|
||||||
|
h.currentReport.MaturityHeight = waitHeight
|
||||||
|
h.reportLock.Unlock()
|
||||||
|
|
||||||
|
if h.hasCLTV() {
|
||||||
|
log.Infof("%T(%x): waiting for CSV and CLTV lock to expire at "+
|
||||||
|
"height %v", h, h.htlc.RHash[:], waitHeight)
|
||||||
|
} else {
|
||||||
|
log.Infof("%T(%x): waiting for CSV lock to expire at height %v",
|
||||||
|
h, h.htlc.RHash[:], waitHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll use this input index to determine the second-level output
|
||||||
|
// index on the transaction, as the signatures requires the indexes to
|
||||||
|
// be the same. We don't look for the second-level output script
|
||||||
|
// directly, as there might be more than one HTLC output to the same
|
||||||
|
// pkScript.
|
||||||
|
op := &wire.OutPoint{
|
||||||
|
Hash: *commitSpend.SpenderTxHash,
|
||||||
|
Index: commitSpend.SpenderInputIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the sweeper sweep the second-level output now that the
|
||||||
|
// CSV/CLTV locks have expired.
|
||||||
|
var witType input.StandardWitnessType
|
||||||
|
if h.isTaproot() {
|
||||||
|
witType = input.TaprootHtlcAcceptedSuccessSecondLevel
|
||||||
|
} else {
|
||||||
|
witType = input.HtlcAcceptedSuccessSecondLevel
|
||||||
|
}
|
||||||
|
inp := h.makeSweepInput(
|
||||||
|
op, witType,
|
||||||
|
input.LeaseHtlcAcceptedSuccessSecondLevel,
|
||||||
|
&h.htlcResolution.SweepSignDesc,
|
||||||
|
h.htlcResolution.CsvDelay, uint32(commitSpend.SpendingHeight),
|
||||||
|
h.htlc.RHash, h.htlcResolution.ResolutionBlob,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculate the budget for this sweep.
|
||||||
|
budget := calculateBudget(
|
||||||
|
btcutil.Amount(inp.SignDesc().Output.Value),
|
||||||
|
h.Budget.NoDeadlineHTLCRatio,
|
||||||
|
h.Budget.NoDeadlineHTLC,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Infof("%T(%x): offering second-level success tx output to sweeper "+
|
||||||
|
"with no deadline and budget=%v at height=%v", h,
|
||||||
|
h.htlc.RHash[:], budget, waitHeight)
|
||||||
|
|
||||||
|
// TODO(yy): use the result chan returned from SweepInput.
|
||||||
|
_, err = h.Sweeper.SweepInput(
|
||||||
|
inp,
|
||||||
|
sweep.Params{
|
||||||
|
Budget: budget,
|
||||||
|
|
||||||
|
// For second level success tx, there's no rush to get
|
||||||
|
// it confirmed, so we use a nil deadline.
|
||||||
|
DeadlineHeight: fn.None[int32](),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user