mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
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:
parent
9c1e6941c3
commit
dc7d90c16f
6 changed files with 56 additions and 155 deletions
|
@ -331,7 +331,6 @@ func TestContractInsertionRetrieval(t *testing.T) {
|
||||||
htlc: channeldb.HTLC{
|
htlc: channeldb.HTLC{
|
||||||
RHash: testPreimage,
|
RHash: testPreimage,
|
||||||
},
|
},
|
||||||
sweepTx: nil,
|
|
||||||
}
|
}
|
||||||
resolvers := []ContractResolver{
|
resolvers := []ContractResolver{
|
||||||
&timeoutResolver,
|
&timeoutResolver,
|
||||||
|
|
|
@ -156,14 +156,6 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
|
||||||
return result, nil
|
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 {
|
func (s *mockSweeper) RelayFeePerKW() chainfee.SatPerKWeight {
|
||||||
return 253
|
return 253
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,13 +51,6 @@ type htlcSuccessResolver struct {
|
||||||
// historical queries to the chain for spends/confirmations.
|
// historical queries to the chain for spends/confirmations.
|
||||||
broadcastHeight uint32
|
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 contains information on the htlc that we are resolving on-chain.
|
||||||
htlc channeldb.HTLC
|
htlc channeldb.HTLC
|
||||||
|
|
||||||
|
@ -427,108 +420,75 @@ func (h *htlcSuccessResolver) broadcastReSignedSuccessTx() (
|
||||||
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
func (h *htlcSuccessResolver) resolveRemoteCommitOutput() (
|
||||||
ContractResolver, error) {
|
ContractResolver, error) {
|
||||||
|
|
||||||
// If we don't already have the sweep transaction constructed, we'll do
|
isTaproot := txscript.IsPayToTaproot(
|
||||||
// so and broadcast it.
|
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||||
if h.sweepTx == nil {
|
)
|
||||||
log.Infof("%T(%x): crafting sweep tx for incoming+remote "+
|
|
||||||
"htlc confirmed", h, h.htlc.RHash[:])
|
|
||||||
|
|
||||||
isTaproot := txscript.IsPayToTaproot(
|
// Before we can craft out sweeping transaction, we need to
|
||||||
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
// create an input which contains all the items required to add
|
||||||
)
|
// this input to a sweeping transaction, and generate a
|
||||||
|
// witness.
|
||||||
// Before we can craft out sweeping transaction, we need to
|
var inp input.Input
|
||||||
// create an input which contains all the items required to add
|
if isTaproot {
|
||||||
// this input to a sweeping transaction, and generate a
|
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
||||||
// witness.
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
var inp input.Input
|
&h.htlcResolution.SweepSignDesc,
|
||||||
if isTaproot {
|
h.htlcResolution.Preimage[:],
|
||||||
inp = lnutils.Ptr(input.MakeTaprootHtlcSucceedInput(
|
h.broadcastHeight,
|
||||||
&h.htlcResolution.ClaimOutpoint,
|
h.htlcResolution.CsvDelay,
|
||||||
&h.htlcResolution.SweepSignDesc,
|
))
|
||||||
h.htlcResolution.Preimage[:],
|
} else {
|
||||||
h.broadcastHeight,
|
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
||||||
h.htlcResolution.CsvDelay,
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
))
|
&h.htlcResolution.SweepSignDesc,
|
||||||
} else {
|
h.htlcResolution.Preimage[:],
|
||||||
inp = lnutils.Ptr(input.MakeHtlcSucceedInput(
|
h.broadcastHeight,
|
||||||
&h.htlcResolution.ClaimOutpoint,
|
h.htlcResolution.CsvDelay,
|
||||||
&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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the confirmation notification before broadcasting the sweep
|
// Calculate the budget for this sweep.
|
||||||
// transaction.
|
budget := calculateBudget(
|
||||||
sweepTXID := h.sweepTx.TxHash()
|
btcutil.Amount(inp.SignDesc().Output.Value),
|
||||||
sweepScript := h.sweepTx.TxOut[0].PkScript
|
h.Budget.DeadlineHTLCRatio,
|
||||||
confNtfn, err := h.Notifier.RegisterConfirmationsNtfn(
|
h.Budget.DeadlineHTLC,
|
||||||
&sweepTXID, sweepScript, 1, h.broadcastHeight,
|
)
|
||||||
|
|
||||||
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regardless of whether an existing transaction was found or newly
|
// Wait for the direct-preimage HTLC sweep tx to confirm.
|
||||||
// constructed, we'll broadcast the sweep transaction to the network.
|
sweepTxDetails, err := waitForSpend(
|
||||||
label := labels.MakeLabel(
|
&h.htlcResolution.ClaimOutpoint,
|
||||||
labels.LabelTypeChannelClose, &h.ShortChanID,
|
h.htlcResolution.SweepSignDesc.Output.PkScript,
|
||||||
|
h.broadcastHeight, h.Notifier, h.quit,
|
||||||
)
|
)
|
||||||
err = h.PublishTx(h.sweepTx, label)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("%T(%x): unable to publish tx: %v",
|
|
||||||
h, h.htlc.RHash[:], err)
|
|
||||||
confNtfn.Cancel()
|
|
||||||
|
|
||||||
return nil, err
|
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
|
// Once the transaction has received a sufficient number of
|
||||||
// confirmations, we'll mark ourselves as fully resolved and exit.
|
// confirmations, we'll mark ourselves as fully resolved and exit.
|
||||||
h.resolved = true
|
h.resolved = true
|
||||||
|
|
||||||
// Checkpoint the resolver, and write the outcome to disk.
|
// Checkpoint the resolver, and write the outcome to disk.
|
||||||
return nil, h.checkpointClaim(
|
return nil, h.checkpointClaim(
|
||||||
&sweepTXID,
|
sweepTxDetails.SpenderTxHash,
|
||||||
channeldb.ResolverOutcomeClaimed,
|
channeldb.ResolverOutcomeClaimed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,17 +181,14 @@ func TestHtlcSuccessSingleStage(t *testing.T) {
|
||||||
// that our sweep succeeded.
|
// that our sweep succeeded.
|
||||||
preCheckpoint: func(ctx *htlcResolverTestContext,
|
preCheckpoint: func(ctx *htlcResolverTestContext,
|
||||||
_ bool) error {
|
_ 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.
|
// The resolver will offer the input to the
|
||||||
ctx.notifier.ConfChan <- &chainntnfs.TxConfirmation{
|
// sweeper.
|
||||||
Tx: sweepTx,
|
details := &chainntnfs.SpendDetail{
|
||||||
BlockHeight: testInitialBlockHeight - 1,
|
SpendingTx: sweepTx,
|
||||||
|
SpenderTxHash: &sweepTxid,
|
||||||
}
|
}
|
||||||
|
ctx.notifier.SpendChan <- details
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
|
@ -50,12 +50,6 @@ type UtxoSweeper interface {
|
||||||
SweepInput(input input.Input, params sweep.Params) (chan sweep.Result,
|
SweepInput(input input.Input, params sweep.Params) (chan sweep.Result,
|
||||||
error)
|
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
|
// RelayFeePerKW returns the minimum fee rate required for transactions
|
||||||
// to be relayed.
|
// to be relayed.
|
||||||
RelayFeePerKW() chainfee.SatPerKWeight
|
RelayFeePerKW() chainfee.SatPerKWeight
|
||||||
|
|
|
@ -1159,47 +1159,6 @@ func (s *UtxoSweeper) handleUpdateReq(req *updateReq) (
|
||||||
return resultChan, nil
|
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.
|
// ListSweeps returns a list of the sweeps recorded by the sweep store.
|
||||||
func (s *UtxoSweeper) ListSweeps() ([]chainhash.Hash, error) {
|
func (s *UtxoSweeper) ListSweeps() ([]chainhash.Hash, error) {
|
||||||
return s.cfg.Store.ListSweeps()
|
return s.cfg.Store.ListSweeps()
|
||||||
|
|
Loading…
Add table
Reference in a new issue