multi: standardize sweep/change addr support

pull/107/head
Oliver Gugger 5 months ago
parent 7e3ea44fd4
commit a05962e03e
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -166,11 +166,21 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
sweepAddr string, publish bool, feeRate uint32, minExpiry, sweepAddr string, publish bool, feeRate uint32, minExpiry,
maxNumBlocks, maxNumAccounts, maxNumBatchKeys uint32) error { maxNumBlocks, maxNumAccounts, maxNumBatchKeys uint32) error {
signer := &lnd.Signer{ var (
ExtendedKey: extendedKey, estimator input.TxWeightEstimator
ChainParams: chainParams, signer = &lnd.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
api = newExplorerAPI(apiURL)
)
sweepScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil {
return err
} }
api := newExplorerAPI(apiURL)
tx, err := api.Transaction(outpoint.Hash.String()) tx, err := api.Transaction(outpoint.Hash.String())
if err != nil { if err != nil {
@ -246,7 +256,6 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
// Calculate the fee based on the given fee rate and our weight // Calculate the fee based on the given fee rate and our weight
// estimation. // estimation.
var ( var (
estimator input.TxWeightEstimator
prevOutFetcher = txscript.NewCannedPrevOutputFetcher( prevOutFetcher = txscript.NewCannedPrevOutputFetcher(
pkScript, sweepValue, pkScript, sweepValue,
) )
@ -282,15 +291,10 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
signDesc.HashType = txscript.SigHashDefault signDesc.HashType = txscript.SigHashDefault
signDesc.SignMethod = input.TaprootScriptSpendSignMethod signDesc.SignMethod = input.TaprootScriptSpendSignMethod
} }
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight())) totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
// Add our sweep destination output. // Add our sweep destination output.
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
if err != nil {
return err
}
sweepTx.TxOut = []*wire.TxOut{{ sweepTx.TxOut = []*wire.TxOut{{
Value: sweepValue - int64(totalFee), Value: sweepValue - int64(totalFee),
PkScript: sweepScript, PkScript: sweepScript,

@ -105,8 +105,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
privKeys := make([]*secp256k1.PrivateKey, 0, len(c.InputOutpoints)) privKeys := make([]*secp256k1.PrivateKey, 0, len(c.InputOutpoints))
// Get the addresses for the inputs. // Get the addresses for the inputs.
for _, input := range c.InputOutpoints { for _, inputOutpoint := range c.InputOutpoints {
addrString, err := api.Address(input) addrString, err := api.Address(inputOutpoint)
if err != nil { if err != nil {
return err return err
} }
@ -118,12 +118,12 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
addresses = append(addresses, addr) addresses = append(addresses, addr)
txHash, err := chainhash.NewHashFromStr(input[:64]) txHash, err := chainhash.NewHashFromStr(inputOutpoint[:64])
if err != nil { if err != nil {
return err return err
} }
vout, err := strconv.Atoi(input[65:]) vout, err := strconv.Atoi(inputOutpoint[65:])
if err != nil { if err != nil {
return err return err
} }
@ -144,7 +144,13 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
} }
// Start with the txweight estimator. // Start with the txweight estimator.
estimator := input.TxWeightEstimator{} var estimator input.TxWeightEstimator
sweepScript, err := lnd.PrepareWalletAddress(
c.SweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil {
return err
}
// Find the key for the given addresses and add their // Find the key for the given addresses and add their
// output weight to the tx estimator. // output weight to the tx estimator.
@ -169,7 +175,9 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
return err return err
} }
estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) estimator.AddTaprootKeySpendInput(
txscript.SigHashDefault,
)
default: default:
return fmt.Errorf("address type %T not supported", addr) return fmt.Errorf("address type %T not supported", addr)
@ -189,47 +197,32 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
// Next get the full value of the inputs. // Next get the full value of the inputs.
var totalInput btcutil.Amount var totalInput btcutil.Amount
for _, input := range outpoints { for _, outpoint := range outpoints {
// Get the transaction. // Get the transaction.
tx, err := api.Transaction(input.Hash.String()) tx, err := api.Transaction(outpoint.Hash.String())
if err != nil { if err != nil {
return err return err
} }
value := tx.Vout[input.Index].Value value := tx.Vout[outpoint.Index].Value
// Get the output index. // Get the output index.
totalInput += btcutil.Amount(value) totalInput += btcutil.Amount(value)
scriptPubkey, err := hex.DecodeString(tx.Vout[input.Index].ScriptPubkey) scriptPubkey, err := hex.DecodeString(
tx.Vout[outpoint.Index].ScriptPubkey,
)
if err != nil { if err != nil {
return err return err
} }
// Add the output to the map. // Add the output to the map.
prevOuts[*input] = &wire.TxOut{ prevOuts[*outpoint] = &wire.TxOut{
Value: int64(value), Value: int64(value),
PkScript: scriptPubkey, PkScript: scriptPubkey,
} }
} }
// Calculate the fee.
sweepAddr, err := btcutil.DecodeAddress(c.SweepAddr, chainParams)
if err != nil {
return err
}
switch sweepAddr.(type) {
case *btcutil.AddressWitnessPubKeyHash:
estimator.AddP2WKHOutput()
case *btcutil.AddressTaproot:
estimator.AddP2TROutput()
default:
return fmt.Errorf("address type %T not supported", sweepAddr)
}
// Calculate the fee. // Calculate the fee.
feeRateKWeight := chainfee.SatPerKVByte(1000 * c.FeeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * c.FeeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight())) totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
@ -238,14 +231,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
tx := wire.NewMsgTx(2) tx := wire.NewMsgTx(2)
// Add the inputs. // Add the inputs.
for _, input := range outpoints { for _, outpoint := range outpoints {
tx.AddTxIn(wire.NewTxIn(input, nil, nil)) tx.AddTxIn(wire.NewTxIn(outpoint, nil, nil))
}
// Add the output.
sweepScript, err := txscript.PayToAddrScript(sweepAddr)
if err != nil {
return err
} }
tx.AddTxOut(wire.NewTxOut(int64(totalInput-totalFee), sweepScript)) tx.AddTxOut(wire.NewTxOut(int64(totalInput-totalFee), sweepScript))
@ -285,7 +272,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
} }
default: default:
return fmt.Errorf("address type %T not supported", addresses[i]) return fmt.Errorf("address type %T not supported",
addresses[i])
} }
} }
@ -296,7 +284,7 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
} }
// Print the transaction. // Print the transaction.
fmt.Printf("Sweeping transaction:\n%s\n", hex.EncodeToString(txBuf.Bytes())) fmt.Printf("Sweeping transaction:\n%x\n", txBuf.Bytes())
// Publish the transaction. // Publish the transaction.
if c.Publish { if c.Publish {

@ -115,17 +115,12 @@ func (c *pullAnchorCommand) Execute(_ *cobra.Command, _ []string) error {
err) err)
} }
changeScript, err := lnd.GetP2WPKHScript(c.ChangeAddr, chainParams)
if err != nil {
return fmt.Errorf("error parsing change addr: %w", err)
}
// Set default values. // Set default values.
if c.FeeRate == 0 { if c.FeeRate == 0 {
c.FeeRate = defaultFeeSatPerVByte c.FeeRate = defaultFeeSatPerVByte
} }
return createPullTransactionTemplate( return createPullTransactionTemplate(
extendedKey, c.APIURL, outpoint, c.AnchorAddrs, changeScript, extendedKey, c.APIURL, outpoint, c.AnchorAddrs, c.ChangeAddr,
c.FeeRate, c.FeeRate,
) )
} }
@ -141,14 +136,23 @@ type targetAnchor struct {
func createPullTransactionTemplate(rootKey *hdkeychain.ExtendedKey, func createPullTransactionTemplate(rootKey *hdkeychain.ExtendedKey,
apiURL string, sponsorOutpoint *wire.OutPoint, anchorAddrs []string, apiURL string, sponsorOutpoint *wire.OutPoint, anchorAddrs []string,
changeScript []byte, feeRate uint32) error { changeAddr string, feeRate uint32) error {
signer := &lnd.Signer{ var (
ExtendedKey: rootKey, signer = &lnd.Signer{
ChainParams: chainParams, ExtendedKey: rootKey,
ChainParams: chainParams,
}
api = newExplorerAPI(apiURL)
estimator input.TxWeightEstimator
)
changeScript, err := lnd.PrepareWalletAddress(
changeAddr, chainParams, &estimator, rootKey, "change",
)
if err != nil {
return err
} }
api := newExplorerAPI(apiURL)
estimator := input.TxWeightEstimator{}
// Make sure the sponsor input is a P2WPKH or P2TR input and is known // Make sure the sponsor input is a P2WPKH or P2TR input and is known
// to the block explorer, so we can fetch the witness utxo. // to the block explorer, so we can fetch the witness utxo.
@ -209,7 +213,6 @@ func createPullTransactionTemplate(rootKey *hdkeychain.ExtendedKey,
} }
// Now we can calculate the fee and add the change output. // Now we can calculate the fee and add the change output.
estimator.AddP2WKHOutput()
anchorAmt := uint64(len(anchorAddrs)) * 330 anchorAmt := uint64(len(anchorAddrs)) * 330
totalOutputValue := btcutil.Amount(sponsorTxOut.Value + anchorAmt) totalOutputValue := btcutil.Amount(sponsorTxOut.Value + anchorAmt)
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()

@ -5,7 +5,6 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
@ -165,29 +164,20 @@ func (c *recoverLoopInCommand) Execute(_ *cobra.Command, _ []string) error {
} }
// Get the destination address. // Get the destination address.
sweepAddr, err := btcutil.DecodeAddress(c.SweepAddr, chainParams) var estimator input.TxWeightEstimator
sweepScript, err := lnd.PrepareWalletAddress(
c.SweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil { if err != nil {
return err return err
} }
// Calculate the sweep fee. // Calculate the sweep fee.
estimator := &input.TxWeightEstimator{} err = htlc.AddTimeoutToEstimator(&estimator)
err = htlc.AddTimeoutToEstimator(estimator)
if err != nil { if err != nil {
return err return err
} }
switch sweepAddr.(type) {
case *btcutil.AddressWitnessPubKeyHash:
estimator.AddP2WKHOutput()
case *btcutil.AddressTaproot:
estimator.AddP2TROutput()
default:
return fmt.Errorf("unsupported address type")
}
feeRateKWeight := chainfee.SatPerKVByte( feeRateKWeight := chainfee.SatPerKVByte(
1000 * c.FeeRate, 1000 * c.FeeRate,
).FeePerKWeight() ).FeePerKWeight()
@ -216,13 +206,8 @@ func (c *recoverLoopInCommand) Execute(_ *cobra.Command, _ []string) error {
}) })
// Add output for the destination address. // Add output for the destination address.
sweepPkScript, err := txscript.PayToAddrScript(sweepAddr)
if err != nil {
return err
}
sweepTx.AddTxOut(&wire.TxOut{ sweepTx.AddTxOut(&wire.TxOut{
PkScript: sweepPkScript, PkScript: sweepScript,
Value: int64(loopIn.Contract.AmountRequested) - int64(fee), Value: int64(loopIn.Contract.AmountRequested) - int64(fee),
}) })

@ -236,35 +236,38 @@ func (c *rescueFundingCommand) Execute(_ *cobra.Command, _ []string) error {
return err return err
} }
// Make sure the sweep addr is a P2WKH address so we can do accurate
// fee estimation.
sweepScript, err := lnd.GetP2WPKHScript(c.SweepAddr, chainParams)
if err != nil {
return fmt.Errorf("error parsing sweep addr: %w", err)
}
return rescueFunding( return rescueFunding(
localKeyDesc, remotePubKey, signer, chainOp, localKeyDesc, remotePubKey, signer, chainOp, c.SweepAddr,
sweepScript, btcutil.Amount(c.FeeRate), c.APIURL, btcutil.Amount(c.FeeRate), c.APIURL,
) )
} }
func rescueFunding(localKeyDesc *keychain.KeyDescriptor, func rescueFunding(localKeyDesc *keychain.KeyDescriptor,
remoteKey *btcec.PublicKey, signer *lnd.Signer, remoteKey *btcec.PublicKey, signer *lnd.Signer,
chainPoint *wire.OutPoint, sweepPKScript []byte, feeRate btcutil.Amount, chainPoint *wire.OutPoint, sweepAddr string, feeRate btcutil.Amount,
apiURL string) error { apiURL string) error {
var (
estimator input.TxWeightEstimator
api = newExplorerAPI(apiURL)
)
sweepScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, &estimator, signer.ExtendedKey, "sweep",
)
if err != nil {
return err
}
// Prepare the wire part of the PSBT. // Prepare the wire part of the PSBT.
txIn := &wire.TxIn{ txIn := &wire.TxIn{
PreviousOutPoint: *chainPoint, PreviousOutPoint: *chainPoint,
Sequence: 0, Sequence: 0,
} }
txOut := &wire.TxOut{ txOut := &wire.TxOut{
PkScript: sweepPKScript, PkScript: sweepScript,
} }
// Locate the output in the funding TX. // Locate the output in the funding TX.
api := newExplorerAPI(apiURL)
tx, err := api.Transaction(chainPoint.Hash.String()) tx, err := api.Transaction(chainPoint.Hash.String())
if err != nil { if err != nil {
return fmt.Errorf("error fetching UTXO info for outpoint %s: "+ return fmt.Errorf("error fetching UTXO info for outpoint %s: "+
@ -303,17 +306,15 @@ func rescueFunding(localKeyDesc *keychain.KeyDescriptor,
WitnessScript: witnessScript, WitnessScript: witnessScript,
Unknowns: []*psbt.Unknown{{ Unknowns: []*psbt.Unknown{{
// We add the public key the other party needs to sign // We add the public key the other party needs to sign
// with as a proprietary field so we can easily read it // with as a proprietary field, so we can easily read it
// out with the signrescuefunding command. // out with the signrescuefunding command.
Key: PsbtKeyTypeOutputMissingSigPubkey, Key: PsbtKeyTypeOutputMissingSigPubkey,
Value: remoteKey.SerializeCompressed(), Value: remoteKey.SerializeCompressed(),
}}, }},
} }
// Estimate the transaction weight so we can do the fee estimation. // Estimate the transaction weight, so we can do the fee estimation.
var estimator input.TxWeightEstimator
estimator.AddWitnessInput(MultiSigWitnessSize) estimator.AddWitnessInput(MultiSigWitnessSize)
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight())) totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
txOut.Value = utxo.Value - int64(totalFee) txOut.Value = utxo.Value - int64(totalFee)

@ -133,6 +133,14 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
sweepAddr string, recoveryWindow uint32, feeRate uint32, sweepAddr string, recoveryWindow uint32, feeRate uint32,
publish bool) error { publish bool) error {
var estimator input.TxWeightEstimator
sweepScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil {
return err
}
var ( var (
targets []*targetAddr targets []*targetAddr
api = newExplorerAPI(apiURL) api = newExplorerAPI(apiURL)
@ -177,7 +185,6 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
// Create estimator and transaction template. // Create estimator and transaction template.
var ( var (
estimator input.TxWeightEstimator
signDescs []*input.SignDescriptor signDescs []*input.SignDescriptor
sweepTx = wire.NewMsgTx(2) sweepTx = wire.NewMsgTx(2)
totalOutputValue = uint64(0) totalOutputValue = uint64(0)
@ -292,13 +299,6 @@ func sweepRemoteClosed(extendedKey *hdkeychain.ExtendedKey, apiURL,
len(targets), totalOutputValue, sweepDustLimit) len(targets), totalOutputValue, sweepDustLimit)
} }
// Add our sweep destination output.
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
if err != nil {
return err
}
estimator.AddP2WKHOutput()
// Calculate the fee based on the given fee rate and our weight // Calculate the fee based on the given fee rate and our weight
// estimation. // estimation.
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()

@ -221,18 +221,26 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiURL string,
publish bool, feeRate uint32) error { publish bool, feeRate uint32) error {
// Create signer and transaction template. // Create signer and transaction template.
signer := &lnd.Signer{ var (
ExtendedKey: extendedKey, estimator input.TxWeightEstimator
ChainParams: chainParams, signer = &lnd.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
api = newExplorerAPI(apiURL)
)
sweepScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil {
return err
} }
api := newExplorerAPI(apiURL)
var ( var (
sweepTx = wire.NewMsgTx(2) sweepTx = wire.NewMsgTx(2)
totalOutputValue = int64(0) totalOutputValue = int64(0)
signDescs = make([]*input.SignDescriptor, 0) signDescs = make([]*input.SignDescriptor, 0)
prevOutFetcher = txscript.NewMultiPrevOutFetcher(nil) prevOutFetcher = txscript.NewMultiPrevOutFetcher(nil)
estimator input.TxWeightEstimator
) )
for _, target := range targets { for _, target := range targets {
// We can't rely on the CSV delay of the channel DB to be // We can't rely on the CSV delay of the channel DB to be
@ -247,8 +255,8 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiURL string,
), target.lockScript, 0, maxCsvTimeout, ), target.lockScript, 0, maxCsvTimeout,
) )
if err != nil { if err != nil {
log.Errorf("Could not create matching script for %s "+ log.Errorf("could not create matching script for %s "+
"or csv too high: %w", target.channelPoint, err) "or csv too high: %v", target.channelPoint, err)
continue continue
} }
@ -288,13 +296,6 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiURL string,
estimator.AddWitnessInput(input.ToLocalTimeoutWitnessSize) estimator.AddWitnessInput(input.ToLocalTimeoutWitnessSize)
} }
// Add our sweep destination output.
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
if err != nil {
return err
}
estimator.AddP2WKHOutput()
// Calculate the fee based on the given fee rate and our weight // Calculate the fee based on the given fee rate and our weight
// estimation. // estimation.
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()

@ -248,12 +248,30 @@ func sweepTimeLockManual(extendedKey *hdkeychain.ExtendedKey, apiURL string,
maxCsvTimeout, startNumChannels, maxNumChannels, maxCsvTimeout, startNumChannels, maxNumChannels,
maxNumChanUpdates) maxNumChanUpdates)
// Create signer and transaction template.
var (
estimator input.TxWeightEstimator
signer = &lnd.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
api = newExplorerAPI(apiURL)
)
// First of all, we need to parse the lock addr and make sure we can // First of all, we need to parse the lock addr and make sure we can
// brute force the script with the information we have. If not, we can't // brute force the script with the information we have. If not, we can't
// continue anyway. // continue anyway.
lockScript, err := lnd.GetP2WSHScript(timeLockAddr, chainParams) lockScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, nil, extendedKey, "time lock",
)
if err != nil { if err != nil {
return fmt.Errorf("invalid time lock addr: %w", err) return err
}
sweepScript, err := lnd.PrepareWalletAddress(
sweepAddr, chainParams, &estimator, extendedKey, "sweep",
)
if err != nil {
return err
} }
// We need to go through a lot of our keys so it makes sense to // We need to go through a lot of our keys so it makes sense to
@ -303,13 +321,6 @@ func sweepTimeLockManual(extendedKey *hdkeychain.ExtendedKey, apiURL string,
return fmt.Errorf("target script not derived") return fmt.Errorf("target script not derived")
} }
// Create signer and transaction template.
signer := &lnd.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
}
api := newExplorerAPI(apiURL)
// We now know everything we need to construct the sweep transaction, // We now know everything we need to construct the sweep transaction,
// except for what outpoint to sweep. We'll ask the chain API to give // except for what outpoint to sweep. We'll ask the chain API to give
// us this information. // us this information.
@ -339,17 +350,11 @@ func sweepTimeLockManual(extendedKey *hdkeychain.ExtendedKey, apiURL string,
// Calculate the fee based on the given fee rate and our weight // Calculate the fee based on the given fee rate and our weight
// estimation. // estimation.
var estimator input.TxWeightEstimator
estimator.AddWitnessInput(input.ToLocalTimeoutWitnessSize) estimator.AddWitnessInput(input.ToLocalTimeoutWitnessSize)
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight() feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight())) totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
// Add our sweep destination output. // Add our sweep destination output.
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
if err != nil {
return err
}
sweepTx.TxOut = []*wire.TxOut{{ sweepTx.TxOut = []*wire.TxOut{{
Value: sweepValue - int64(totalFee), Value: sweepValue - int64(totalFee),
PkScript: sweepScript, PkScript: sweepScript,

@ -13,6 +13,7 @@ import (
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/shachain" "github.com/lightningnetwork/lnd/shachain"
@ -437,6 +438,67 @@ func CheckAddress(addr string, chainParams *chaincfg.Params, allowDerive bool,
return nil return nil
} }
func PrepareWalletAddress(addr string, chainParams *chaincfg.Params,
estimator *input.TxWeightEstimator, rootKey *hdkeychain.ExtendedKey,
hint string) ([]byte, error) {
// We already checked if deriving a new address is allowed in a previous
// step, so we can just go ahead and do it now if requested.
if addr == AddressDeriveFromWallet {
// To maximize compatibility and recoverability, we always
// derive the very first P2WKH address from the wallet.
// This corresponds to the derivation path: m/84'/0'/0'/0/0.
derivedKey, err := DeriveChildren(rootKey, []uint32{
HardenedKeyStart + waddrmgr.KeyScopeBIP0084.Purpose,
HardenedKeyStart + chainParams.HDCoinType,
HardenedKeyStart + 0, 0, 0,
})
if err != nil {
return nil, err
}
derivedPubKey, err := derivedKey.ECPubKey()
if err != nil {
return nil, err
}
p2wkhAddr, err := P2WKHAddr(derivedPubKey, chainParams)
if err != nil {
return nil, err
}
return txscript.PayToAddrScript(p2wkhAddr)
}
parsedAddr, err := ParseAddress(addr, chainParams)
if err != nil {
return nil, fmt.Errorf("%s address is invalid: %w", hint, err)
}
// Exit early if we don't need to estimate the weight.
if estimator == nil {
return txscript.PayToAddrScript(parsedAddr)
}
// These are the three address types that we support in general. We
// should have checked that we get the correct type in a previous step.
switch parsedAddr.(type) {
case *btcutil.AddressWitnessPubKeyHash:
estimator.AddP2WKHOutput()
case *btcutil.AddressWitnessScriptHash:
estimator.AddP2WSHOutput()
case *btcutil.AddressTaproot:
estimator.AddP2TROutput()
default:
return nil, fmt.Errorf("%s address is of wrong type", hint)
}
return txscript.PayToAddrScript(parsedAddr)
}
func matchAddrType(addr btcutil.Address, allowedTypes ...AddrType) bool { func matchAddrType(addr btcutil.Address, allowedTypes ...AddrType) bool {
contains := func(allowedTypes []AddrType, addrType AddrType) bool { contains := func(allowedTypes []AddrType, addrType AddrType) bool {
for _, allowedType := range allowedTypes { for _, allowedType := range allowedTypes {

Loading…
Cancel
Save