loop/sweep/sweeper.go
carla 435238ac03
mutli: bump lndclient version
We need the newer version of lndclient to get our synced to chain
state. This requires changes to our test mock, so it is separated
into a different commit. Since lndclient is expanding to be used in
projects other than loop, the client interface is embedded in our
mock rather than implementing functions we do not need. Functions that
we already use in tests are updated accordingly.

Note that while this commit bumps us to a version of lndclient that
defaults to requiring lnd 0.11, we do not bump our required version
yet because we are not using any new endpoints.
2020-08-14 09:25:39 +02:00

122 lines
3 KiB
Go

package sweep
import (
"context"
"fmt"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop/swap"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain"
)
// Sweeper creates htlc sweep txes.
type Sweeper struct {
Lnd *lndclient.LndServices
}
// CreateSweepTx creates an htlc sweep tx.
func (s *Sweeper) CreateSweepTx(
globalCtx context.Context, height int32,
htlc *swap.Htlc, htlcOutpoint wire.OutPoint,
keyBytes [33]byte,
witnessFunc func(sig []byte) (wire.TxWitness, error),
amount, fee btcutil.Amount,
destAddr btcutil.Address) (*wire.MsgTx, error) {
// Compose tx.
sweepTx := wire.NewMsgTx(2)
sweepTx.LockTime = uint32(height)
// Add HTLC input.
sweepTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: htlcOutpoint,
SignatureScript: htlc.SigScript,
})
// Add output for the destination address.
sweepPkScript, err := txscript.PayToAddrScript(destAddr)
if err != nil {
return nil, err
}
sweepTx.AddTxOut(&wire.TxOut{
PkScript: sweepPkScript,
Value: int64(amount - fee),
})
// Generate a signature for the swap htlc transaction.
key, err := btcec.ParsePubKey(keyBytes[:], btcec.S256())
if err != nil {
return nil, err
}
signDesc := lndclient.SignDescriptor{
WitnessScript: htlc.Script,
Output: &wire.TxOut{
Value: int64(amount),
},
HashType: txscript.SigHashAll,
InputIndex: 0,
KeyDesc: keychain.KeyDescriptor{
PubKey: key,
},
}
rawSigs, err := s.Lnd.Signer.SignOutputRaw(
globalCtx, sweepTx, []*lndclient.SignDescriptor{&signDesc},
)
if err != nil {
return nil, fmt.Errorf("signing: %v", err)
}
sig := rawSigs[0]
// Add witness stack to the tx input.
sweepTx.TxIn[0].Witness, err = witnessFunc(sig)
if err != nil {
return nil, err
}
return sweepTx, nil
}
// GetSweepFee calculates the required tx fee to spend to P2WKH. It takes a
// function that is expected to add the weight of the input to the weight
// estimator.
func (s *Sweeper) GetSweepFee(ctx context.Context,
addInputEstimate func(*input.TxWeightEstimator),
destAddr btcutil.Address, sweepConfTarget int32) (
btcutil.Amount, error) {
// Get fee estimate from lnd.
feeRate, err := s.Lnd.WalletKit.EstimateFee(ctx, sweepConfTarget)
if err != nil {
return 0, fmt.Errorf("estimate fee: %v", err)
}
// Calculate weight for this tx.
var weightEstimate input.TxWeightEstimator
switch destAddr.(type) {
case *btcutil.AddressWitnessScriptHash:
weightEstimate.AddP2WSHOutput()
case *btcutil.AddressWitnessPubKeyHash:
weightEstimate.AddP2WKHOutput()
case *btcutil.AddressScriptHash:
weightEstimate.AddP2SHOutput()
case *btcutil.AddressPubKeyHash:
weightEstimate.AddP2PKHOutput()
default:
return 0, fmt.Errorf("unknown address type %T", destAddr)
}
addInputEstimate(&weightEstimate)
weight := weightEstimate.Weight()
return feeRate.FeeForWeight(int64(weight)), nil
}