mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-04-06 02:58:03 +02:00
contractcourt: add resolver handlers in htlcSuccessResolver
This commit refactors the `Resolve` method by adding two resolver handlers to handle waiting for spending confirmations.
This commit is contained in:
parent
fb499bc4cc
commit
c92d7f0fd0
@ -140,27 +140,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
||||
|
||||
// To wrap this up, we'll wait until the second-level transaction has
|
||||
// been spent, then fully resolve the contract.
|
||||
log.Infof("%T(%x): waiting for second-level HTLC output to be spent "+
|
||||
"after csv_delay=%v", h, h.htlc.RHash[:], h.htlcResolution.CsvDelay)
|
||||
|
||||
spend, err := waitForSpend(
|
||||
secondLevelOutpoint,
|
||||
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||
h.broadcastHeight, h.Notifier, h.quit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.reportLock.Lock()
|
||||
h.currentReport.RecoveredBalance = h.currentReport.LimboBalance
|
||||
h.currentReport.LimboBalance = 0
|
||||
h.reportLock.Unlock()
|
||||
|
||||
h.resolved = true
|
||||
return nil, h.checkpointClaim(
|
||||
spend.SpenderTxHash, channeldb.ResolverOutcomeClaimed,
|
||||
)
|
||||
return nil, h.resolveSuccessTxOutput(*secondLevelOutpoint)
|
||||
}
|
||||
|
||||
// broadcastSuccessTx handles an HTLC output on our local commitment by
|
||||
@ -187,40 +167,11 @@ func (h *htlcSuccessResolver) broadcastSuccessTx() (
|
||||
|
||||
// We'll now broadcast the second layer transaction so we can kick off
|
||||
// the claiming process.
|
||||
//
|
||||
// TODO(roasbeef): after changing sighashes send to tx bundler
|
||||
label := labels.MakeLabel(
|
||||
labels.LabelTypeChannelClose, &h.ShortChanID,
|
||||
)
|
||||
err := h.PublishTx(h.htlcResolution.SignedSuccessTx, label)
|
||||
err := h.resolveLegacySuccessTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Otherwise, this is an output on our commitment transaction. In this
|
||||
// case, we'll send it to the incubator, but only if we haven't already
|
||||
// done so.
|
||||
if !h.outputIncubating {
|
||||
log.Infof("%T(%x): incubating incoming htlc output",
|
||||
h, h.htlc.RHash[:])
|
||||
|
||||
err := h.IncubateOutputs(
|
||||
h.ChanPoint, fn.None[lnwallet.OutgoingHtlcResolution](),
|
||||
fn.Some(h.htlcResolution),
|
||||
h.broadcastHeight, fn.Some(int32(h.htlc.RefundTimeout)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h.outputIncubating = true
|
||||
|
||||
if err := h.Checkpoint(h); err != nil {
|
||||
log.Errorf("unable to Checkpoint: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &h.htlcResolution.ClaimOutpoint, nil
|
||||
}
|
||||
|
||||
@ -242,33 +193,25 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (*wire.OutPoint,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("%T(%x): waiting for second-level HTLC success "+
|
||||
"transaction to confirm", h, h.htlc.RHash[:])
|
||||
|
||||
// Wait for the second level transaction to confirm.
|
||||
commitSpend, err = waitForSpend(
|
||||
&h.htlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint,
|
||||
h.htlcResolution.SignDetails.SignDesc.Output.PkScript,
|
||||
h.broadcastHeight, h.Notifier, h.quit,
|
||||
)
|
||||
err = h.resolveSuccessTx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now that the second-level transaction has confirmed, we
|
||||
// checkpoint the state so we'll go to the next stage in case
|
||||
// of restarts.
|
||||
h.outputIncubating = true
|
||||
if err := h.Checkpoint(h); err != nil {
|
||||
log.Errorf("unable to Checkpoint: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Infof("%T(%x): second-level HTLC success transaction "+
|
||||
"confirmed!", h, h.htlc.RHash[:])
|
||||
}
|
||||
|
||||
err := h.sweepSuccessTxOutput()
|
||||
// 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 nil, err
|
||||
}
|
||||
|
||||
err = h.sweepSuccessTxOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -304,23 +247,14 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Once the transaction has received a sufficient number of
|
||||
// confirmations, we'll mark ourselves as fully resolved and exit.
|
||||
h.resolved = true
|
||||
|
||||
// Checkpoint the resolver, and write the outcome to disk.
|
||||
return nil, h.checkpointClaim(
|
||||
sweepTxDetails.SpenderTxHash,
|
||||
channeldb.ResolverOutcomeClaimed,
|
||||
)
|
||||
return nil, h.checkpointClaim(sweepTxDetails.SpenderTxHash)
|
||||
}
|
||||
|
||||
// checkpointClaim checkpoints the success resolver with the reports it needs.
|
||||
// If this htlc was claimed two stages, it will write reports for both stages,
|
||||
// otherwise it will just write for the single htlc claim.
|
||||
func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash,
|
||||
outcome channeldb.ResolverOutcome) error {
|
||||
|
||||
func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash) error {
|
||||
// Mark the htlc as final settled.
|
||||
err := h.ChainArbitratorConfig.PutFinalHtlcOutcome(
|
||||
h.ChannelArbitratorConfig.ShortChanID, h.htlc.HtlcIndex, true,
|
||||
@ -348,7 +282,7 @@ func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash,
|
||||
OutPoint: h.htlcResolution.ClaimOutpoint,
|
||||
Amount: amt,
|
||||
ResolverType: channeldb.ResolverTypeIncomingHtlc,
|
||||
ResolverOutcome: outcome,
|
||||
ResolverOutcome: channeldb.ResolverOutcomeClaimed,
|
||||
SpendTxID: spendTx,
|
||||
},
|
||||
}
|
||||
@ -373,6 +307,7 @@ func (h *htlcSuccessResolver) checkpointClaim(spendTx *chainhash.Hash,
|
||||
}
|
||||
|
||||
// Finally, we checkpoint the resolver with our report(s).
|
||||
h.resolved = true
|
||||
return h.Checkpoint(h, reports...)
|
||||
}
|
||||
|
||||
@ -748,3 +683,129 @@ func (h *htlcSuccessResolver) sweepSuccessTxOutput() error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// resolveLegacySuccessTx handles an HTLC output from a pre-anchor type channel
|
||||
// by broadcasting the second-level success transaction.
|
||||
func (h *htlcSuccessResolver) resolveLegacySuccessTx() error {
|
||||
// Otherwise we'll publish the second-level transaction directly and
|
||||
// offer the resolution to the nursery to handle.
|
||||
h.log.Infof("broadcasting legacy second-level success tx: %v",
|
||||
h.htlcResolution.SignedSuccessTx.TxHash())
|
||||
|
||||
// We'll now broadcast the second layer transaction so we can kick off
|
||||
// the claiming process.
|
||||
//
|
||||
// TODO(yy): offer it to the sweeper instead.
|
||||
label := labels.MakeLabel(
|
||||
labels.LabelTypeChannelClose, &h.ShortChanID,
|
||||
)
|
||||
err := h.PublishTx(h.htlcResolution.SignedSuccessTx, label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fast-forward to resolve the output from the success tx if the it has
|
||||
// already been sent to the UtxoNursery.
|
||||
if h.outputIncubating {
|
||||
return h.resolveSuccessTxOutput(h.htlcResolution.ClaimOutpoint)
|
||||
}
|
||||
|
||||
h.log.Infof("incubating incoming htlc output")
|
||||
|
||||
// Send the output to the incubator.
|
||||
err = h.IncubateOutputs(
|
||||
h.ChanPoint, fn.None[lnwallet.OutgoingHtlcResolution](),
|
||||
fn.Some(h.htlcResolution),
|
||||
h.broadcastHeight, fn.Some(int32(h.htlc.RefundTimeout)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark the output as incubating and checkpoint it.
|
||||
h.outputIncubating = true
|
||||
if err := h.Checkpoint(h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Move to resolve the output.
|
||||
return h.resolveSuccessTxOutput(h.htlcResolution.ClaimOutpoint)
|
||||
}
|
||||
|
||||
// resolveSuccessTx waits for the sweeping tx of the second-level success tx to
|
||||
// confirm and offers the output from the success tx to the sweeper.
|
||||
func (h *htlcSuccessResolver) resolveSuccessTx() error {
|
||||
h.log.Infof("waiting for 2nd-level HTLC success transaction to confirm")
|
||||
|
||||
// Create aliases to make the code more readable.
|
||||
outpoint := h.htlcResolution.SignedSuccessTx.TxIn[0].PreviousOutPoint
|
||||
pkScript := h.htlcResolution.SignDetails.SignDesc.Output.PkScript
|
||||
|
||||
// Wait for the second level transaction to confirm.
|
||||
commitSpend, err := waitForSpend(
|
||||
&outpoint, pkScript, h.broadcastHeight, h.Notifier, h.quit,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
// If the 2nd-stage sweeping has already been started, we can
|
||||
// fast-forward to start the resolving process for the stage two
|
||||
// output.
|
||||
if h.outputIncubating {
|
||||
return h.resolveSuccessTxOutput(op)
|
||||
}
|
||||
|
||||
// Now that the second-level transaction has confirmed, we checkpoint
|
||||
// the state so we'll go to the next stage in case of restarts.
|
||||
h.outputIncubating = true
|
||||
if err := h.Checkpoint(h); err != nil {
|
||||
log.Errorf("unable to Checkpoint: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
h.log.Infof("2nd-level HTLC success tx=%v confirmed",
|
||||
commitSpend.SpenderTxHash)
|
||||
|
||||
// Send the sweep request for the output from the success tx.
|
||||
if err := h.sweepSuccessTxOutput(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return h.resolveSuccessTxOutput(op)
|
||||
}
|
||||
|
||||
// resolveSuccessTxOutput waits for the spend of the output from the 2nd-level
|
||||
// success tx.
|
||||
func (h *htlcSuccessResolver) resolveSuccessTxOutput(op wire.OutPoint) error {
|
||||
// To wrap this up, we'll wait until the second-level transaction has
|
||||
// been spent, then fully resolve the contract.
|
||||
log.Infof("%T(%x): waiting for second-level HTLC output to be spent "+
|
||||
"after csv_delay=%v", h, h.htlc.RHash[:],
|
||||
h.htlcResolution.CsvDelay)
|
||||
|
||||
spend, err := waitForSpend(
|
||||
&op, h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||
h.broadcastHeight, h.Notifier, h.quit,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.reportLock.Lock()
|
||||
h.currentReport.RecoveredBalance = h.currentReport.LimboBalance
|
||||
h.currentReport.LimboBalance = 0
|
||||
h.reportLock.Unlock()
|
||||
|
||||
return h.checkpointClaim(spend.SpenderTxHash)
|
||||
}
|
||||
|
@ -548,9 +548,9 @@ func (h *htlcTimeoutResolver) sweepSecondLevelTx() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// sendSecondLevelTxLegacy sends a second level timeout transaction to the utxo
|
||||
// nursery. This transaction uses the legacy SIGHASH_ALL flag.
|
||||
func (h *htlcTimeoutResolver) sendSecondLevelTxLegacy() error {
|
||||
// resolveSecondLevelTxLegacy sends a second level timeout transaction to the
|
||||
// utxo nursery. This transaction uses the legacy SIGHASH_ALL flag.
|
||||
func (h *htlcTimeoutResolver) resolveSecondLevelTxLegacy() error {
|
||||
log.Debugf("%T(%v): incubating htlc output", h,
|
||||
h.htlcResolution.ClaimOutpoint)
|
||||
|
||||
@ -654,7 +654,7 @@ func (h *htlcTimeoutResolver) spendHtlcOutput() (
|
||||
// commitment for a non-anchor channel, so we'll send it to the utxo
|
||||
// nursery.
|
||||
case h.isLegacyOutput() && !h.outputIncubating:
|
||||
if err := h.sendSecondLevelTxLegacy(); err != nil {
|
||||
if err := h.resolveSecondLevelTxLegacy(); err != nil {
|
||||
log.Errorf("Sending timeout tx to nursery: %v", err)
|
||||
|
||||
return nil, err
|
||||
|
Loading…
x
Reference in New Issue
Block a user