multi: modify sweeper.CreateSweepTx to accept conf target, style changes
In this commit, we modify the newly introduced UtxoSweeper.CreateSweepTx to accept the confirmation target as a param of the method rather than a struct level variable. We do this as this allows each caller to decide at sweep time, what the fee rate should be, rather than using a global value that is meant to work in all scenarios. For example, anytime we're sweeping an output with a CLTV lock that's has a dependant transaction we need to sweep/cancel, we may require a higher fee rate than a regular force close with a CSV output.
This commit is contained in:
parent
90fe860a3c
commit
fc21bf091a
@ -5,10 +5,11 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||
@ -20,6 +21,12 @@ var (
|
||||
endian = binary.BigEndian
|
||||
)
|
||||
|
||||
const (
|
||||
// sweepConfTarget is the default number of blocks that we'll use as a
|
||||
// confirmation target when sweeping.
|
||||
sweepConfTarget = 6
|
||||
)
|
||||
|
||||
// ContractResolver is an interface which packages a state machine which is
|
||||
// able to carry out the necessary steps required to fully resolve a Bitcoin
|
||||
// contract on-chain. Resolvers are fully encodable to ensure callers are able
|
||||
@ -446,19 +453,27 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
|
||||
"incoming+remote htlc confirmed", h,
|
||||
h.payHash[:])
|
||||
|
||||
// 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.
|
||||
input := sweep.MakeHtlcSucceedInput(
|
||||
&h.htlcResolution.ClaimOutpoint,
|
||||
&h.htlcResolution.SweepSignDesc,
|
||||
h.htlcResolution.Preimage[:],
|
||||
)
|
||||
|
||||
// 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.
|
||||
var err error
|
||||
|
||||
// TODO: Set tx lock time to current block height instead of
|
||||
// zero. Will be taken care of once sweeper implementation is
|
||||
// complete.
|
||||
h.sweepTx, err = h.Sweeper.CreateSweepTx(
|
||||
[]sweep.Input{&input}, 0)
|
||||
[]sweep.Input{&input}, sweepConfTarget, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1216,16 +1231,26 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
|
||||
// If the sweep transaction isn't already generated, and the remote
|
||||
// party broadcast the commitment transaction then we'll create it now.
|
||||
case c.sweepTx == nil && !isLocalCommitTx:
|
||||
// As we haven't already generated the sweeping transaction,
|
||||
// we'll now craft an input with all the information required
|
||||
// to create a fully valid sweeping transaction to recover
|
||||
// these coins.
|
||||
input := sweep.MakeBaseInput(
|
||||
&c.commitResolution.SelfOutPoint,
|
||||
lnwallet.CommitmentNoDelay,
|
||||
&c.commitResolution.SelfOutputSignDesc)
|
||||
&c.commitResolution.SelfOutputSignDesc,
|
||||
)
|
||||
|
||||
// With out input constructed, we'll now request that the
|
||||
// sweeper construct a valid sweeping transaction for this
|
||||
// input.
|
||||
//
|
||||
// TODO: Set tx lock time to current block height instead of
|
||||
// zero. Will be taken care of once sweeper implementation is
|
||||
// complete.
|
||||
c.sweepTx, err = c.Sweeper.CreateSweepTx(
|
||||
[]sweep.Input{&input}, 0)
|
||||
[]sweep.Input{&input}, sweepConfTarget, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
"image/color"
|
||||
"math/big"
|
||||
"net"
|
||||
@ -16,6 +15,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||
"github.com/btcsuite/btcd/connmgr"
|
||||
@ -59,10 +60,6 @@ const (
|
||||
// durations exceeding this value will be eligible to have their
|
||||
// backoffs reduced.
|
||||
defaultStableConnDuration = 10 * time.Minute
|
||||
|
||||
// sweepTxConfirmationTarget assigns a confirmation target for sweep
|
||||
// txes on which the fee calculation will be based.
|
||||
sweepTxConfirmationTarget = 6
|
||||
)
|
||||
|
||||
var (
|
||||
@ -592,12 +589,12 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
|
||||
return newSweepPkScript(cc.wallet)
|
||||
},
|
||||
Signer: cc.wallet.Cfg.Signer,
|
||||
ConfTarget: sweepTxConfirmationTarget,
|
||||
})
|
||||
|
||||
s.utxoNursery = newUtxoNursery(&NurseryConfig{
|
||||
ChainIO: cc.chainIO,
|
||||
ConfDepth: 1,
|
||||
SweepTxConfTarget: 6,
|
||||
FetchClosedChannels: chanDB.FetchClosedChannels,
|
||||
FetchClosedChannel: chanDB.FetchClosedChannel,
|
||||
Notifier: cc.chainNotifier,
|
||||
|
@ -16,9 +16,9 @@ type Input interface {
|
||||
// be generated in order to spend this output.
|
||||
WitnessType() lnwallet.WitnessType
|
||||
|
||||
// SignDesc returns a reference to a spendable output's sign descriptor,
|
||||
// which is used during signing to compute a valid witness that spends
|
||||
// this output.
|
||||
// SignDesc returns a reference to a spendable output's sign
|
||||
// descriptor, which is used during signing to compute a valid witness
|
||||
// that spends this output.
|
||||
SignDesc() *lnwallet.SignDescriptor
|
||||
|
||||
// BuildWitness returns a valid witness allowing this output to be
|
||||
@ -41,8 +41,8 @@ type inputKit struct {
|
||||
signDesc lnwallet.SignDescriptor
|
||||
}
|
||||
|
||||
// OutPoint returns the breached output's identifier that is to be included as a
|
||||
// transaction input.
|
||||
// OutPoint returns the breached output's identifier that is to be included as
|
||||
// a transaction input.
|
||||
func (i *inputKit) OutPoint() *wire.OutPoint {
|
||||
return &i.outpoint
|
||||
}
|
||||
@ -67,8 +67,7 @@ type BaseInput struct {
|
||||
|
||||
// MakeBaseInput assembles a new BaseInput that can be used to construct a
|
||||
// sweep transaction.
|
||||
func MakeBaseInput(outpoint *wire.OutPoint,
|
||||
witnessType lnwallet.WitnessType,
|
||||
func MakeBaseInput(outpoint *wire.OutPoint, witnessType lnwallet.WitnessType,
|
||||
signDescriptor *lnwallet.SignDescriptor) BaseInput {
|
||||
|
||||
return BaseInput{
|
||||
@ -82,8 +81,8 @@ func MakeBaseInput(outpoint *wire.OutPoint,
|
||||
|
||||
// BuildWitness computes a valid witness that allows us to spend from the
|
||||
// breached output. It does so by generating the witness generation function,
|
||||
// which is parameterized primarily by the witness type and sign descriptor. The
|
||||
// method then returns the witness computed by invoking this function.
|
||||
// which is parameterized primarily by the witness type and sign descriptor.
|
||||
// The method then returns the witness computed by invoking this function.
|
||||
func (bi *BaseInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||
hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) {
|
||||
|
||||
@ -102,8 +101,8 @@ func (bi *BaseInput) BlocksToMaturity() uint32 {
|
||||
}
|
||||
|
||||
// HtlcSucceedInput constitutes a sweep input that needs a pre-image. The input
|
||||
// is expected to reside on the commitment tx of the remote party and should not
|
||||
// be a second level tx output.
|
||||
// is expected to reside on the commitment tx of the remote party and should
|
||||
// not be a second level tx output.
|
||||
type HtlcSucceedInput struct {
|
||||
inputKit
|
||||
|
||||
@ -127,8 +126,8 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
|
||||
}
|
||||
|
||||
// BuildWitness computes a valid witness that allows us to spend from the
|
||||
// breached output. For HtlcSpendInput it will need to make the preimage part of
|
||||
// the witness.
|
||||
// breached output. For HtlcSpendInput it will need to make the preimage part
|
||||
// of the witness.
|
||||
func (h *HtlcSucceedInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||
hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) {
|
||||
|
||||
@ -149,6 +148,7 @@ func (h *HtlcSucceedInput) BlocksToMaturity() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Add compile-time constraint ensuring input structs implement Input interface.
|
||||
// Compile-time constraints to ensure each input struct implement the Input
|
||||
// interface.
|
||||
var _ Input = (*BaseInput)(nil)
|
||||
var _ Input = (*HtlcSucceedInput)(nil)
|
||||
|
16
sweep/log.go
16
sweep/log.go
@ -5,9 +5,9 @@ import (
|
||||
"github.com/lightningnetwork/lnd/build"
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
// log is a logger that is initialized with no output filters. This means the
|
||||
// package will not perform any logging by default until the caller requests
|
||||
// it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
@ -15,15 +15,15 @@ func init() {
|
||||
UseLogger(build.NewSubLogger("SWPR", nil))
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until UseLogger is called.
|
||||
// DisableLog disables all library log output. Logging output is disabled by
|
||||
// default until UseLogger is called.
|
||||
func DisableLog() {
|
||||
UseLogger(btclog.Disabled)
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
// UseLogger uses a specified Logger to output package logging info. This
|
||||
// should be used in preference to SetLogWriter if the caller is also using
|
||||
// btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
|
@ -8,30 +8,27 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
)
|
||||
|
||||
// UtxoSweeper provides the functionality to generate sweep txes. The plan is to
|
||||
// extend UtxoSweeper in the future to also manage the actual sweeping process
|
||||
// by itself.
|
||||
// UtxoSweeper provides the functionality to generate sweep txes. The plan is
|
||||
// to extend UtxoSweeper in the future to also manage the actual sweeping
|
||||
// process by itself.
|
||||
type UtxoSweeper struct {
|
||||
cfg *UtxoSweeperConfig
|
||||
}
|
||||
|
||||
// UtxoSweeperConfig contains dependencies of UtxoSweeper.
|
||||
type UtxoSweeperConfig struct {
|
||||
// GenSweepScript generates a P2WKH script belonging to the wallet where
|
||||
// funds can be swept.
|
||||
// GenSweepScript generates a P2WKH script belonging to the wallet
|
||||
// where funds can be swept.
|
||||
GenSweepScript func() ([]byte, error)
|
||||
|
||||
// Estimator is used when crafting sweep transactions to estimate the
|
||||
// necessary fee relative to the expected size of the sweep transaction.
|
||||
// necessary fee relative to the expected size of the sweep
|
||||
// transaction.
|
||||
Estimator lnwallet.FeeEstimator
|
||||
|
||||
// Signer is used by the sweeper to generate valid witnesses at the
|
||||
// time the incubated outputs need to be spent.
|
||||
Signer lnwallet.Signer
|
||||
|
||||
// ConfTarget specifies a target for the number of blocks until an
|
||||
// initial confirmation.
|
||||
ConfTarget uint32
|
||||
}
|
||||
|
||||
// New returns a new UtxoSweeper instance.
|
||||
@ -45,18 +42,18 @@ func New(cfg *UtxoSweeperConfig) *UtxoSweeper {
|
||||
// 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 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
|
||||
// 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.
|
||||
func (s *UtxoSweeper) CreateSweepTx(inputs []Input,
|
||||
func (s *UtxoSweeper) CreateSweepTx(inputs []Input, confTarget uint32,
|
||||
currentBlockHeight uint32) (*wire.MsgTx, error) {
|
||||
|
||||
// Generate the receiving script to which the funds will be swept.
|
||||
@ -66,7 +63,7 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []Input,
|
||||
}
|
||||
|
||||
// Using the txn weight estimate, compute the required txn fee.
|
||||
feePerKw, err := s.cfg.Estimator.EstimateFeePerKW(s.cfg.ConfTarget)
|
||||
feePerKw, err := s.cfg.Estimator.EstimateFeePerKW(confTarget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -100,7 +97,6 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []Input,
|
||||
|
||||
// Add all inputs to the sweep transaction. Ensure that for each
|
||||
// csvInput, we set the sequence number properly.
|
||||
|
||||
for _, input := range inputs {
|
||||
sweepTx.AddTxIn(&wire.TxIn{
|
||||
PreviousOutPoint: *input.OutPoint(),
|
||||
@ -110,9 +106,10 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []Input,
|
||||
|
||||
// Before signing the transaction, check to ensure that it meets some
|
||||
// basic validity requirements.
|
||||
// TODO(conner): add more control to sanity checks, allowing us to delay
|
||||
// spending "problem" outputs, e.g. possibly batching with other classes
|
||||
// if fees are too low.
|
||||
//
|
||||
// TODO(conner): add more control to sanity checks, allowing us to
|
||||
// delay spending "problem" outputs, e.g. possibly batching with other
|
||||
// classes if fees are too low.
|
||||
btx := btcutil.NewTx(sweepTx)
|
||||
if err := blockchain.CheckTransactionSanity(btx); err != nil {
|
||||
return nil, err
|
||||
@ -149,13 +146,11 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []Input,
|
||||
// getWeightEstimate returns a weight estimate for the given inputs.
|
||||
// Additionally, it returns counts for the number of csv and cltv inputs.
|
||||
func (s *UtxoSweeper) getWeightEstimate(inputs []Input) ([]Input, int64, int, int) {
|
||||
|
||||
// Create a transaction which sweeps all the newly mature outputs into
|
||||
// an output controlled by the wallet.
|
||||
|
||||
// We initialize a weight estimator so we can accurately asses the
|
||||
// amount of fees we need to pay for this sweep transaction.
|
||||
//
|
||||
// TODO(roasbeef): can be more intelligent about buffering outputs to
|
||||
// be more efficient on-chain.
|
||||
|
||||
var weightEstimate lnwallet.TxWeightEstimator
|
||||
|
||||
// Our sweep transaction will pay to a single segwit p2wkh address,
|
||||
@ -165,10 +160,10 @@ func (s *UtxoSweeper) getWeightEstimate(inputs []Input) ([]Input, int64, int, in
|
||||
// For each output, use its witness type to determine the estimate
|
||||
// weight of its witness, and add it to the proper set of spendable
|
||||
// outputs.
|
||||
csvCount := 0
|
||||
cltvCount := 0
|
||||
|
||||
var sweepInputs []Input
|
||||
var (
|
||||
sweepInputs []Input
|
||||
csvCount, cltvCount int
|
||||
)
|
||||
for i := range inputs {
|
||||
input := inputs[i]
|
||||
|
||||
|
@ -4,11 +4,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/lightningnetwork/lnd/sweep"
|
||||
|
||||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
@ -182,6 +183,10 @@ type NurseryConfig struct {
|
||||
// determining outputs in the chain as confirmed.
|
||||
ConfDepth uint32
|
||||
|
||||
// SweepTxConfTarget assigns a confirmation target for sweep txes on
|
||||
// which the fee calculation will be based.
|
||||
SweepTxConfTarget uint32
|
||||
|
||||
// FetchClosedChannels provides access to a user's channels, such that
|
||||
// they can be marked fully closed after incubation has concluded.
|
||||
FetchClosedChannels func(pendingOnly bool) (
|
||||
@ -871,15 +876,15 @@ func (u *utxoNursery) graduateClass(classHeight uint32) error {
|
||||
// generated a sweep txn for this height. Generate one if there
|
||||
// are kindergarten outputs or cltv crib outputs to be spent.
|
||||
if len(kgtnOutputs) > 0 {
|
||||
sweepInputs := make([]sweep.Input,
|
||||
len(kgtnOutputs))
|
||||
sweepInputs := make([]sweep.Input, len(kgtnOutputs))
|
||||
for i := range kgtnOutputs {
|
||||
sweepInputs[i] = &kgtnOutputs[i]
|
||||
}
|
||||
|
||||
finalTx, err = u.cfg.Sweeper.CreateSweepTx(
|
||||
sweepInputs, classHeight)
|
||||
|
||||
sweepInputs, u.cfg.SweepTxConfTarget,
|
||||
classHeight,
|
||||
)
|
||||
if err != nil {
|
||||
utxnLog.Errorf("Failed to create sweep txn at "+
|
||||
"height=%d", classHeight)
|
||||
|
Loading…
Reference in New Issue
Block a user