contractcourt+sweep: offer direct-preimage spend via SweepInput

This commit removes the method `CreateSweepTx` and makes sure when
sweeping the htlc output via the direct-preimage spend, it's offered via
the `SweepInput` interface.
This commit is contained in:
yyforyongyu 2024-03-19 12:32:01 +08:00
parent 9c1e6941c3
commit dc7d90c16f
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
6 changed files with 56 additions and 155 deletions

View file

@ -331,7 +331,6 @@ func TestContractInsertionRetrieval(t *testing.T) {
htlc: channeldb.HTLC{
RHash: testPreimage,
},
sweepTx: nil,
}
resolvers := []ContractResolver{
&timeoutResolver,

View file

@ -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
}

View file

@ -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,12 +420,6 @@ 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,
)
@ -460,75 +447,48 @@ func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
))
}
// 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,
// 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
}
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.
}
// 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,
// 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,
)
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,
)
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,
)
}

View file

@ -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
},

View file

@ -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

View file

@ -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()