mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-28 10:41:57 +01: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:
parent
10e5a43e46
commit
fb499bc4cc
@ -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
|
||||
// it to confirm, if we haven't already.
|
||||
if !h.outputIncubating {
|
||||
var secondLevelInput input.HtlcSecondLevelAnchorInput
|
||||
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,
|
||||
},
|
||||
)
|
||||
err := h.sweepSuccessTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -316,99 +268,18 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
||||
"confirmed!", h, h.htlc.RHash[:])
|
||||
}
|
||||
|
||||
// If we ended up here after a restart, we must again get the
|
||||
// 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](),
|
||||
},
|
||||
)
|
||||
err := h.sweepSuccessTxOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Will return this outpoint, when this is spent the resolver is fully
|
||||
// resolved.
|
||||
op := &wire.OutPoint{
|
||||
Hash: *commitSpend.SpenderTxHash,
|
||||
Index: commitSpend.SpenderInputIndex,
|
||||
}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
@ -418,53 +289,7 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
||||
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
||||
ContractResolver, 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,
|
||||
},
|
||||
)
|
||||
err := h.sweepRemoteCommitOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -731,3 +556,195 @@ func (h *htlcSuccessResolver) isTaproot() bool {
|
||||
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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user