From 9d33b0008271d889a88fe8937efec1d5eb3205f7 Mon Sep 17 00:00:00 2001 From: "Johan T. Halseth" Date: Wed, 9 Dec 2020 12:24:02 +0100 Subject: [PATCH] contractcourt/success_resolver: extract remoteHTLC sweep into resolveRemoteCommitOutput This move the logic for sweeping the HTLC output on the remote commitment into its own method. --- contractcourt/htlc_success_resolver.go | 199 +++++++++++++------------ 1 file changed, 103 insertions(+), 96 deletions(-) diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 172f3279..5fb034c8 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -105,102 +105,7 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { // If we don't have a success transaction, then this means that this is // an output on the remote party's commitment transaction. if h.htlcResolution.SignedSuccessTx == nil { - // If we don't already have the sweep transaction constructed, - // we'll do so and broadcast it. - if h.sweepTx == nil { - log.Infof("%T(%x): crafting sweep tx for "+ - "incoming+remote htlc confirmed", h, - h.htlc.RHash[:]) - - // 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. - inp := 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 transaction, that we'll use to move these - // coins back into the backing wallet. - // - // TODO: Set tx lock time to current block height - // instead of zero. Will be taken care of once sweeper - // implementation is complete. - // - // TODO: Use time-based sweeper and result chan. - var err error - h.sweepTx, err = h.Sweeper.CreateSweepTx( - []input.Input{&inp}, - sweep.FeePreference{ - ConfTarget: sweepConfTarget, - }, 0, - ) - if err != nil { - return nil, err - } - - log.Infof("%T(%x): crafted sweep tx=%v", h, - h.htlc.RHash[:], spew.Sdump(h.sweepTx)) - - // With the sweep transaction signed, we'll now - // Checkpoint our state. - if err := h.Checkpoint(h); err != nil { - log.Errorf("unable to Checkpoint: %v", err) - return nil, err - } - } - - // Regardless of whether an existing transaction was found or newly - // constructed, we'll broadcast the sweep transaction to the - // network. - label := labels.MakeLabel( - labels.LabelTypeChannelClose, &h.ShortChanID, - ) - err := h.PublishTx(h.sweepTx, label) - if err != nil { - log.Infof("%T(%x): unable to publish tx: %v", - h, h.htlc.RHash[:], err) - return nil, err - } - - // With the sweep transaction broadcast, we'll wait for its - // confirmation. - sweepTXID := h.sweepTx.TxHash() - sweepScript := h.sweepTx.TxOut[0].PkScript - confNtfn, err := h.Notifier.RegisterConfirmationsNtfn( - &sweepTXID, sweepScript, 1, h.broadcastHeight, - ) - if err != nil { - return nil, err - } - - log.Infof("%T(%x): waiting for sweep tx (txid=%v) to be "+ - "confirmed", h, h.htlc.RHash[:], sweepTXID) - - select { - case _, ok := <-confNtfn.Confirmed: - if !ok { - return nil, errResolverShuttingDown - } - - case <-h.quit: - return nil, errResolverShuttingDown - } - - // 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( - &sweepTXID, - channeldb.ResolverOutcomeClaimed, - ) + return h.resolveRemoteCommitOutput() } log.Infof("%T(%x): broadcasting second-layer transition tx: %v", @@ -261,6 +166,108 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { ) } +// resolveRemoteCommitOutput handles sweeping an HTLC output on the remote +// commitment with the preimage. In this case we can sweep the output directly, +// and don't have to broadcast a second-level transaction. +func (h *htlcSuccessResolver) resolveRemoteCommitOutput() ( + ContractResolver, error) { + + // If we don't already have the sweep transaction constructed, we'll do + // so and broadcast it. + if h.sweepTx == nil { + log.Infof("%T(%x): crafting sweep tx for incoming+remote "+ + "htlc confirmed", h, h.htlc.RHash[:]) + + // 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. + inp := 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 + // transaction, that we'll use to move these coins back into + // the backing wallet. + // + // TODO: Set tx lock time to current block height instead of + // zero. Will be taken care of once sweeper implementation is + // complete. + // + // TODO: Use time-based sweeper and result chan. + var err error + h.sweepTx, err = h.Sweeper.CreateSweepTx( + []input.Input{&inp}, + sweep.FeePreference{ + ConfTarget: sweepConfTarget, + }, 0, + ) + if err != nil { + return nil, err + } + + log.Infof("%T(%x): crafted sweep tx=%v", h, + h.htlc.RHash[:], spew.Sdump(h.sweepTx)) + + // With the sweep transaction signed, we'll now Checkpoint our + // state. + if err := h.Checkpoint(h); err != nil { + log.Errorf("unable to Checkpoint: %v", err) + return nil, err + } + } + + // Regardless of whether an existing transaction was found or newly + // constructed, we'll broadcast the sweep transaction to the network. + label := labels.MakeLabel( + labels.LabelTypeChannelClose, &h.ShortChanID, + ) + err := h.PublishTx(h.sweepTx, label) + if err != nil { + log.Infof("%T(%x): unable to publish tx: %v", + h, h.htlc.RHash[:], err) + return nil, err + } + + // With the sweep transaction broadcast, we'll wait for its + // confirmation. + sweepTXID := h.sweepTx.TxHash() + sweepScript := h.sweepTx.TxOut[0].PkScript + confNtfn, err := h.Notifier.RegisterConfirmationsNtfn( + &sweepTXID, sweepScript, 1, h.broadcastHeight, + ) + if err != nil { + return nil, err + } + + log.Infof("%T(%x): waiting for sweep tx (txid=%v) to be "+ + "confirmed", h, h.htlc.RHash[:], sweepTXID) + + select { + case _, ok := <-confNtfn.Confirmed: + if !ok { + return nil, errResolverShuttingDown + } + + case <-h.quit: + return nil, errResolverShuttingDown + } + + // 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( + &sweepTXID, + channeldb.ResolverOutcomeClaimed, + ) +} + // 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.