diff --git a/contractcourt/briefcase_test.go b/contractcourt/briefcase_test.go index a112b7f8c..89e017fd7 100644 --- a/contractcourt/briefcase_test.go +++ b/contractcourt/briefcase_test.go @@ -331,7 +331,6 @@ func TestContractInsertionRetrieval(t *testing.T) { htlc: channeldb.HTLC{ RHash: testPreimage, }, - sweepTx: nil, } resolvers := []ContractResolver{ &timeoutResolver, diff --git a/contractcourt/commit_sweep_resolver_test.go b/contractcourt/commit_sweep_resolver_test.go index 9c6876269..7d42d7be0 100644 --- a/contractcourt/commit_sweep_resolver_test.go +++ b/contractcourt/commit_sweep_resolver_test.go @@ -156,14 +156,6 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) ( return result, nil } -func (s *mockSweeper) CreateSweepTx(inputs []input.Input, - feePref sweep.FeeEstimateInfo) (*wire.MsgTx, error) { - - // We will wait for the test to supply the sweep tx to return. - sweepTx := <-s.createSweepTxChan - return sweepTx, nil -} - func (s *mockSweeper) RelayFeePerKW() chainfee.SatPerKWeight { return 253 } diff --git a/contractcourt/htlc_success_resolver.go b/contractcourt/htlc_success_resolver.go index 9483938b3..cf2894501 100644 --- a/contractcourt/htlc_success_resolver.go +++ b/contractcourt/htlc_success_resolver.go @@ -51,13 +51,6 @@ type htlcSuccessResolver struct { // historical queries to the chain for spends/confirmations. broadcastHeight uint32 - // sweepTx will be non-nil if we've already crafted a transaction to - // sweep a direct HTLC output. This is only a concern if we're sweeping - // from the commitment transaction of the remote party. - // - // TODO(roasbeef): send off to utxobundler - sweepTx *wire.MsgTx - // htlc contains information on the htlc that we are resolving on-chain. htlc channeldb.HTLC @@ -427,108 +420,75 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() ( 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[:]) + isTaproot := txscript.IsPayToTaproot( + h.htlcResolution.SweepSignDesc.Output.PkScript, + ) - isTaproot := txscript.IsPayToTaproot( - h.htlcResolution.SweepSignDesc.Output.PkScript, - ) - - // 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 isTaproot { - inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput( - &h.htlcResolution.ClaimOutpoint, - &h.htlcResolution.SweepSignDesc, - h.htlcResolution.Preimage[:], - h.broadcastHeight, - h.htlcResolution.CsvDelay, - )) - } else { - inp = lnutils.Ptr(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: Use time-based sweeper and result chan. - var err error - h.sweepTx, err = h.Sweeper.CreateSweepTx( - []input.Input{inp}, - sweep.FeeEstimateInfo{ - ConfTarget: sweepConfTarget, - }, - ) - if err != nil { - return nil, err - } - - log.Infof("%T(%x): crafted sweep tx=%v", h, - h.htlc.RHash[:], spew.Sdump(h.sweepTx)) - - // TODO(halseth): should checkpoint sweep tx to DB? Since after - // a restart we might create a different tx, that will conflict - // with the published one. + // 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 isTaproot { + inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput( + &h.htlcResolution.ClaimOutpoint, + &h.htlcResolution.SweepSignDesc, + h.htlcResolution.Preimage[:], + h.broadcastHeight, + h.htlcResolution.CsvDelay, + )) + } else { + inp = lnutils.Ptr(input.MakeHtlcSucceedInput( + &h.htlcResolution.ClaimOutpoint, + &h.htlcResolution.SweepSignDesc, + h.htlcResolution.Preimage[:], + h.broadcastHeight, + h.htlcResolution.CsvDelay, + )) } - // Register the confirmation notification before broadcasting the sweep - // transaction. - sweepTXID := h.sweepTx.TxHash() - sweepScript := h.sweepTx.TxOut[0].PkScript - confNtfn, err := h.Notifier.RegisterConfirmationsNtfn( - &sweepTXID, sweepScript, 1, h.broadcastHeight, + // 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 { 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, + // Wait for the direct-preimage HTLC sweep tx to confirm. + sweepTxDetails, err := waitForSpend( + &h.htlcResolution.ClaimOutpoint, + h.htlcResolution.SweepSignDesc.Output.PkScript, + h.broadcastHeight, h.Notifier, h.quit, ) - err = h.PublishTx(h.sweepTx, label) if err != nil { - log.Infof("%T(%x): unable to publish tx: %v", - h, h.htlc.RHash[:], err) - confNtfn.Cancel() - 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, + sweepTxDetails.SpenderTxHash, channeldb.ResolverOutcomeClaimed, ) } diff --git a/contractcourt/htlc_success_resolver_test.go b/contractcourt/htlc_success_resolver_test.go index 0160dfb52..c8ba57d93 100644 --- a/contractcourt/htlc_success_resolver_test.go +++ b/contractcourt/htlc_success_resolver_test.go @@ -181,17 +181,14 @@ func TestHtlcSuccessSingleStage(t *testing.T) { // that our sweep succeeded. preCheckpoint: func(ctx *htlcResolverTestContext, _ bool) error { - // The resolver will create and publish a sweep - // tx. - resolver := ctx.resolver.(*htlcSuccessResolver) - resolver.Sweeper.(*mockSweeper). - createSweepTxChan <- sweepTx - // Confirm the sweep, which should resolve it. - ctx.notifier.ConfChan <- &chainntnfs.TxConfirmation{ - Tx: sweepTx, - BlockHeight: testInitialBlockHeight - 1, + // The resolver will offer the input to the + // sweeper. + details := &chainntnfs.SpendDetail{ + SpendingTx: sweepTx, + SpenderTxHash: &sweepTxid, } + ctx.notifier.SpendChan <- details return nil }, diff --git a/contractcourt/interfaces.go b/contractcourt/interfaces.go index bdcb6c5de..c1b0211ba 100644 --- a/contractcourt/interfaces.go +++ b/contractcourt/interfaces.go @@ -50,12 +50,6 @@ type UtxoSweeper interface { SweepInput(input input.Input, params sweep.Params) (chan sweep.Result, error) - // CreateSweepTx accepts a list of inputs and signs and generates a txn - // that spends from them. This method also makes an accurate fee - // estimate before generating the required witnesses. - CreateSweepTx(inputs []input.Input, - feePref sweep.FeeEstimateInfo) (*wire.MsgTx, error) - // RelayFeePerKW returns the minimum fee rate required for transactions // to be relayed. RelayFeePerKW() chainfee.SatPerKWeight diff --git a/sweep/sweeper.go b/sweep/sweeper.go index a50342191..9252fb2fa 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -1159,47 +1159,6 @@ func (s *UtxoSweeper) handleUpdateReq(req *updateReq) ( return resultChan, nil } -// CreateSweepTx accepts a list of inputs and signs and generates a txn that -// spends from them. This method also makes an accurate fee estimate before -// generating the required witnesses. -// -// The created transaction has a single output sending all the funds back to -// the source wallet, after accounting for the fee estimate. -// -// The value of currentBlockHeight argument will be set as the tx locktime. -// This function assumes that all CLTV inputs will be unlocked after -// currentBlockHeight. Reasons not to use the maximum of all actual CLTV expiry -// values of the inputs: -// -// - Make handling re-orgs easier. -// - Thwart future possible fee sniping attempts. -// - Make us blend in with the bitcoind wallet. -// -// TODO(yy): remove this method and only allow sweeping via requests. -func (s *UtxoSweeper) CreateSweepTx(inputs []input.Input, - feePref FeeEstimateInfo) (*wire.MsgTx, error) { - - feePerKw, err := feePref.Estimate( - s.cfg.FeeEstimator, s.cfg.MaxFeeRate.FeePerKWeight(), - ) - if err != nil { - return nil, err - } - - // Generate the receiving script to which the funds will be swept. - pkScript, err := s.cfg.GenSweepScript() - if err != nil { - return nil, err - } - - tx, _, err := createSweepTx( - inputs, nil, pkScript, uint32(s.currentHeight), feePerKw, - s.cfg.MaxFeeRate.FeePerKWeight(), s.cfg.Signer, - ) - - return tx, err -} - // ListSweeps returns a list of the sweeps recorded by the sweep store. func (s *UtxoSweeper) ListSweeps() ([]chainhash.Hash, error) { return s.cfg.Store.ListSweeps()