mirror of
https://github.com/lightninglabs/loop
synced 2024-11-08 01:10:29 +00:00
84ae3667d5
It was factored out from GetSweepFee method. It provides fee rate and weight in addition to absolute fee.
240 lines
6.2 KiB
Go
240 lines
6.2 KiB
Go
package sweep
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/btcutil/psbt"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightninglabs/lndclient"
|
|
"github.com/lightninglabs/loop/swap"
|
|
"github.com/lightningnetwork/lnd/input"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
)
|
|
|
|
// Sweeper creates htlc sweep txes.
|
|
type Sweeper struct {
|
|
Lnd *lndclient.LndServices
|
|
}
|
|
|
|
// CreateUnsignedTaprootKeySpendSweepTx creates a taproot htlc sweep tx using
|
|
// keyspend. Returns the raw unsigned txn, the psbt serialized txn, the sighash
|
|
// or an error.
|
|
func (s *Sweeper) CreateUnsignedTaprootKeySpendSweepTx(
|
|
ctx context.Context, lockTime uint32,
|
|
htlc *swap.Htlc, htlcOutpoint wire.OutPoint,
|
|
amount, fee btcutil.Amount, destAddr btcutil.Address) (
|
|
*wire.MsgTx, []byte, []byte, error) {
|
|
|
|
if htlc.Version != swap.HtlcV3 {
|
|
return nil, nil, nil, fmt.Errorf("invalid htlc version")
|
|
}
|
|
|
|
// Add output for the destination address.
|
|
sweepPkScript, err := txscript.PayToAddrScript(destAddr)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
// Compose tx.
|
|
sweepTx := &wire.MsgTx{
|
|
Version: 2,
|
|
TxIn: []*wire.TxIn{{
|
|
PreviousOutPoint: htlcOutpoint,
|
|
}},
|
|
TxOut: []*wire.TxOut{{
|
|
Value: int64(amount - fee),
|
|
PkScript: sweepPkScript,
|
|
}},
|
|
LockTime: lockTime,
|
|
}
|
|
|
|
packet, err := psbt.NewFromUnsignedTx(sweepTx)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
packet.Inputs[0].WitnessUtxo = &wire.TxOut{
|
|
Value: int64(amount),
|
|
PkScript: htlc.PkScript,
|
|
}
|
|
|
|
var psbtBuf bytes.Buffer
|
|
err = packet.Serialize(&psbtBuf)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
// We now need to create the raw sighash of the transaction, as we'll
|
|
// use it to create the witness for our htlc sweep.
|
|
prevOutputFetcher := txscript.NewCannedPrevOutputFetcher(
|
|
htlc.PkScript, int64(amount),
|
|
)
|
|
sigHashes := txscript.NewTxSigHashes(sweepTx, prevOutputFetcher)
|
|
|
|
taprootSigHash, err := txscript.CalcTaprootSignatureHash(
|
|
sigHashes, txscript.SigHashDefault, sweepTx, 0,
|
|
prevOutputFetcher,
|
|
)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
return sweepTx, psbtBuf.Bytes(), taprootSigHash, nil
|
|
}
|
|
|
|
// CreateSweepTx creates an htlc sweep tx.
|
|
func (s *Sweeper) CreateSweepTx(
|
|
globalCtx context.Context, height int32, sequence uint32,
|
|
htlc *swap.Htlc, htlcOutpoint wire.OutPoint,
|
|
keyBytes [33]byte, witnessScript []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,
|
|
Sequence: sequence,
|
|
})
|
|
|
|
// 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[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
signDesc := lndclient.SignDescriptor{
|
|
WitnessScript: witnessScript,
|
|
Output: &wire.TxOut{
|
|
Value: int64(amount),
|
|
PkScript: htlc.PkScript,
|
|
},
|
|
HashType: htlc.SigHash(),
|
|
InputIndex: 0,
|
|
KeyDesc: keychain.KeyDescriptor{
|
|
PubKey: key,
|
|
},
|
|
}
|
|
|
|
// Update the sign method from the default witness_v0 if this is a
|
|
// taproot htlc. Note that we'll always be doing script spend when
|
|
// sweeping a taproot htlc using the CreateSweepTx function.
|
|
if htlc.Version == swap.HtlcV3 {
|
|
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
|
|
}
|
|
|
|
// We need our previous outputs for taproot spends, and there's no
|
|
// harm including them for segwit v0, so we always include our prevOut.
|
|
prevOut := []*wire.TxOut{
|
|
{
|
|
Value: int64(amount),
|
|
PkScript: htlc.PkScript,
|
|
},
|
|
}
|
|
|
|
rawSigs, err := s.Lnd.Signer.SignOutputRaw(
|
|
globalCtx, sweepTx, []*lndclient.SignDescriptor{&signDesc},
|
|
prevOut,
|
|
)
|
|
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) error,
|
|
destAddr btcutil.Address, sweepConfTarget int32) (
|
|
btcutil.Amount, error) {
|
|
|
|
// Use GetSweepFeeDetails to get the fee and other unused data.
|
|
fee, _, _, err := s.GetSweepFeeDetails(
|
|
ctx, addInputEstimate, destAddr, sweepConfTarget,
|
|
)
|
|
|
|
return fee, err
|
|
}
|
|
|
|
// 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. It returns also the fee rate and transaction weight.
|
|
func (s *Sweeper) GetSweepFeeDetails(ctx context.Context,
|
|
addInputEstimate func(*input.TxWeightEstimator) error,
|
|
destAddr btcutil.Address, sweepConfTarget int32) (
|
|
btcutil.Amount, chainfee.SatPerKWeight, lntypes.WeightUnit, error) {
|
|
|
|
// Get fee estimate from lnd.
|
|
feeRate, err := s.Lnd.WalletKit.EstimateFeeRate(ctx, sweepConfTarget)
|
|
if err != nil {
|
|
return 0, 0, 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()
|
|
|
|
case *btcutil.AddressTaproot:
|
|
weightEstimate.AddP2TROutput()
|
|
|
|
default:
|
|
return 0, 0, 0, fmt.Errorf("estimate fee: unknown address "+
|
|
"type %T", destAddr)
|
|
}
|
|
|
|
err = addInputEstimate(&weightEstimate)
|
|
if err != nil {
|
|
return 0, 0, 0, err
|
|
}
|
|
|
|
weight := weightEstimate.Weight()
|
|
|
|
return feeRate.FeeForWeight(weight), feeRate, weight, nil
|
|
}
|