diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 56e035e6..223220d6 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -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 } diff --git a/server.go b/server.go index 7ebe5189..5385b245 100644 --- a/server.go +++ b/server.go @@ -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 ( @@ -591,13 +588,13 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, GenSweepScript: func() ([]byte, error) { return newSweepPkScript(cc.wallet) }, - Signer: cc.wallet.Cfg.Signer, - ConfTarget: sweepTxConfirmationTarget, + Signer: cc.wallet.Cfg.Signer, }) s.utxoNursery = newUtxoNursery(&NurseryConfig{ ChainIO: cc.chainIO, ConfDepth: 1, + SweepTxConfTarget: 6, FetchClosedChannels: chanDB.FetchClosedChannels, FetchClosedChannel: chanDB.FetchClosedChannel, Notifier: cc.chainNotifier, diff --git a/sweep/input.go b/sweep/input.go index 795adc09..6cf09760 100644 --- a/sweep/input.go +++ b/sweep/input.go @@ -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) diff --git a/sweep/log.go b/sweep/log.go index a5112dfb..9bf48116 100644 --- a/sweep/log.go +++ b/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 } diff --git a/sweep/sweeper.go b/sweep/sweeper.go index a3484614..a4c5a439 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -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] diff --git a/utxonursery.go b/utxonursery.go index 8557ad55..5c712b48 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -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)