diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 09a1cecf..56e035e6 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -9,7 +9,6 @@ import ( "io" "io/ioutil" - "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" @@ -447,59 +446,19 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { "incoming+remote htlc confirmed", h, h.payHash[:]) - // In this case, we can sweep it directly from the - // commitment output. We'll first grab a fresh address - // from the wallet to sweep the output. - addr, err := h.NewSweepAddr() - if err != nil { - return nil, err - } - - // With our address obtained, we'll query for an - // estimate to be confirmed at ease. - // - // TODO(roasbeef): signal up if fee would be too large - // to sweep singly, need to batch - feePerKw, err := h.FeeEstimator.EstimateFeePerKW(6) - if err != nil { - return nil, err - } - - log.Debugf("%T(%x): using %v sat/kw to sweep htlc"+ - "incoming+remote htlc confirmed", h, - h.payHash[:], int64(feePerKw)) - - // Using a weight estimator, we'll compute the total - // fee required, and from that the value we'll end up - // with. - totalWeight := (&lnwallet.TxWeightEstimator{}). - AddWitnessInput(lnwallet.OfferedHtlcSuccessWitnessSize). - AddP2WKHOutput().Weight() - totalFees := feePerKw.FeeForWeight(int64(totalWeight)) - sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value - - int64(totalFees) - - // With the fee computation finished, we'll now - // construct the sweep transaction. - htlcPoint := h.htlcResolution.ClaimOutpoint - h.sweepTx = wire.NewMsgTx(2) - h.sweepTx.AddTxIn(&wire.TxIn{ - PreviousOutPoint: htlcPoint, - }) - h.sweepTx.AddTxOut(&wire.TxOut{ - PkScript: addr, - Value: sweepAmt, - }) - - // With the transaction fully assembled, we can now - // generate a valid witness for the transaction. - h.htlcResolution.SweepSignDesc.SigHashes = txscript.NewTxSigHashes( - h.sweepTx, - ) - h.sweepTx.TxIn[0].Witness, err = lnwallet.SenderHtlcSpendRedeem( - h.Signer, &h.htlcResolution.SweepSignDesc, h.sweepTx, + input := sweep.MakeHtlcSucceedInput( + &h.htlcResolution.ClaimOutpoint, + &h.htlcResolution.SweepSignDesc, h.htlcResolution.Preimage[:], ) + + 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) if err != nil { return nil, err } diff --git a/sweep/input.go b/sweep/input.go index 29d27098..795adc09 100644 --- a/sweep/input.go +++ b/sweep/input.go @@ -35,13 +35,36 @@ type Input interface { BlocksToMaturity() uint32 } -// BaseInput contains all the information needed to sweep an output. -type BaseInput struct { +type inputKit struct { outpoint wire.OutPoint witnessType lnwallet.WitnessType signDesc lnwallet.SignDescriptor } +// 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 +} + +// WitnessType returns the type of witness that must be generated to spend the +// breached output. +func (i *inputKit) WitnessType() lnwallet.WitnessType { + return i.witnessType +} + +// SignDesc returns the breached output's SignDescriptor, which is used during +// signing to compute the witness. +func (i *inputKit) SignDesc() *lnwallet.SignDescriptor { + return &i.signDesc +} + +// BaseInput contains all the information needed to sweep a basic output +// (CSV/CLTV/no time lock) +type BaseInput struct { + inputKit +} + // MakeBaseInput assembles a new BaseInput that can be used to construct a // sweep transaction. func MakeBaseInput(outpoint *wire.OutPoint, @@ -49,30 +72,14 @@ func MakeBaseInput(outpoint *wire.OutPoint, signDescriptor *lnwallet.SignDescriptor) BaseInput { return BaseInput{ - outpoint: *outpoint, - witnessType: witnessType, - signDesc: *signDescriptor, + inputKit{ + outpoint: *outpoint, + witnessType: witnessType, + signDesc: *signDescriptor, + }, } } -// OutPoint returns the breached output's identifier that is to be included as a -// transaction input. -func (bi *BaseInput) OutPoint() *wire.OutPoint { - return &bi.outpoint -} - -// WitnessType returns the type of witness that must be generated to spend the -// breached output. -func (bi *BaseInput) WitnessType() lnwallet.WitnessType { - return bi.witnessType -} - -// SignDesc returns the breached output's SignDescriptor, which is used during -// signing to compute the witness. -func (bi *BaseInput) SignDesc() *lnwallet.SignDescriptor { - return &bi.signDesc -} - // 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 @@ -94,6 +101,54 @@ func (bi *BaseInput) BlocksToMaturity() uint32 { return 0 } -// Add compile-time constraint ensuring BaseInput implements -// SpendableOutput. +// 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. +type HtlcSucceedInput struct { + inputKit + + preimage []byte +} + +// MakeHtlcSucceedInput assembles a new redeem input that can be used to +// construct a sweep transaction. +func MakeHtlcSucceedInput(outpoint *wire.OutPoint, + signDescriptor *lnwallet.SignDescriptor, + preimage []byte) HtlcSucceedInput { + + return HtlcSucceedInput{ + inputKit: inputKit{ + outpoint: *outpoint, + witnessType: lnwallet.HtlcAcceptedRemoteSuccess, + signDesc: *signDescriptor, + }, + preimage: preimage, + } +} + +// 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. +func (h *HtlcSucceedInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx, + hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) { + + desc := h.signDesc + desc.SigHashes = hashCache + desc.InputIndex = txinIdx + + return lnwallet.SenderHtlcSpendRedeem( + signer, &desc, txn, + h.preimage, + ) +} + +// BlocksToMaturity returns the relative timelock, as a number of blocks, that +// must be built on top of the confirmation height before the output can be +// spent. +func (h *HtlcSucceedInput) BlocksToMaturity() uint32 { + return 0 +} + +// Add compile-time constraint ensuring input structs implement Input interface. var _ Input = (*BaseInput)(nil) +var _ Input = (*HtlcSucceedInput)(nil) diff --git a/sweep/sweeper.go b/sweep/sweeper.go index 3a2197e3..a4c3f498 100644 --- a/sweep/sweeper.go +++ b/sweep/sweeper.go @@ -118,6 +118,13 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []Input, ) cltvCount++ + // An HTLC on the commitment transaction of the remote party, + // that can be swept with the preimage. + case lnwallet.HtlcAcceptedRemoteSuccess: + weightEstimate.AddWitnessInput( + lnwallet.OfferedHtlcSuccessWitnessSize, + ) + default: unknownCount++ log.Warnf("kindergarten output in nursery store "+