Skip to content

Commit

Permalink
multi: standardize sweep/change addr support
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Jan 4, 2024
1 parent 7e3ea44 commit a05962e
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 135 deletions.
24 changes: 14 additions & 10 deletions cmd/chantools/closepoolaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,21 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
sweepAddr string, publish bool, feeRate uint32, minExpiry,
maxNumBlocks, maxNumAccounts, maxNumBatchKeys uint32) error {

signer := &lnd.Signer{
ExtendedKey: extendedKey,
ChainParams: chainParams,
var (
estimator input.TxWeightEstimator
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())
if err != nil {
Expand Down Expand Up @@ -246,7 +256,6 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
// Calculate the fee based on the given fee rate and our weight
// estimation.
var (
estimator input.TxWeightEstimator
prevOutFetcher = txscript.NewCannedPrevOutputFetcher(
pkScript, sweepValue,
)
Expand Down Expand Up @@ -282,15 +291,10 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
signDesc.HashType = txscript.SigHashDefault
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
}
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))

// Add our sweep destination output.
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
if err != nil {
return err
}
sweepTx.TxOut = []*wire.TxOut{{
Value: sweepValue - int64(totalFee),
PkScript: sweepScript,
Expand Down
64 changes: 26 additions & 38 deletions cmd/chantools/doublespendinputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
privKeys := make([]*secp256k1.PrivateKey, 0, len(c.InputOutpoints))

// Get the addresses for the inputs.
for _, input := range c.InputOutpoints {
addrString, err := api.Address(input)
for _, inputOutpoint := range c.InputOutpoints {
addrString, err := api.Address(inputOutpoint)
if err != nil {
return err
}
Expand All @@ -118,12 +118,12 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {

addresses = append(addresses, addr)

txHash, err := chainhash.NewHashFromStr(input[:64])
txHash, err := chainhash.NewHashFromStr(inputOutpoint[:64])
if err != nil {
return err
}

vout, err := strconv.Atoi(input[65:])
vout, err := strconv.Atoi(inputOutpoint[65:])
if err != nil {
return err
}
Expand All @@ -144,7 +144,13 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
}

// 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
// output weight to the tx estimator.
Expand All @@ -169,7 +175,9 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
return err
}

estimator.AddTaprootKeySpendInput(txscript.SigHashDefault)
estimator.AddTaprootKeySpendInput(
txscript.SigHashDefault,
)

default:
return fmt.Errorf("address type %T not supported", addr)
Expand All @@ -189,47 +197,32 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {

// Next get the full value of the inputs.
var totalInput btcutil.Amount
for _, input := range outpoints {
for _, outpoint := range outpoints {
// Get the transaction.
tx, err := api.Transaction(input.Hash.String())
tx, err := api.Transaction(outpoint.Hash.String())
if err != nil {
return err
}

value := tx.Vout[input.Index].Value
value := tx.Vout[outpoint.Index].Value

// Get the output index.
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 {
return err
}

// Add the output to the map.
prevOuts[*input] = &wire.TxOut{
prevOuts[*outpoint] = &wire.TxOut{
Value: int64(value),
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.
feeRateKWeight := chainfee.SatPerKVByte(1000 * c.FeeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
Expand All @@ -238,14 +231,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
tx := wire.NewMsgTx(2)

// Add the inputs.
for _, input := range outpoints {
tx.AddTxIn(wire.NewTxIn(input, nil, nil))
}

// Add the output.
sweepScript, err := txscript.PayToAddrScript(sweepAddr)
if err != nil {
return err
for _, outpoint := range outpoints {
tx.AddTxIn(wire.NewTxIn(outpoint, nil, nil))
}

tx.AddTxOut(wire.NewTxOut(int64(totalInput-totalFee), sweepScript))
Expand Down Expand Up @@ -285,7 +272,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
}

default:
return fmt.Errorf("address type %T not supported", addresses[i])
return fmt.Errorf("address type %T not supported",
addresses[i])
}
}

Expand All @@ -296,7 +284,7 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
}

// 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.
if c.Publish {
Expand Down
29 changes: 16 additions & 13 deletions cmd/chantools/pullanchor.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,12 @@ func (c *pullAnchorCommand) Execute(_ *cobra.Command, _ []string) error {
err)
}

changeScript, err := lnd.GetP2WPKHScript(c.ChangeAddr, chainParams)
if err != nil {
return fmt.Errorf("error parsing change addr: %w", err)
}

// Set default values.
if c.FeeRate == 0 {
c.FeeRate = defaultFeeSatPerVByte
}
return createPullTransactionTemplate(
extendedKey, c.APIURL, outpoint, c.AnchorAddrs, changeScript,
extendedKey, c.APIURL, outpoint, c.AnchorAddrs, c.ChangeAddr,
c.FeeRate,
)
}
Expand All @@ -141,14 +136,23 @@ type targetAnchor struct {

func createPullTransactionTemplate(rootKey *hdkeychain.ExtendedKey,
apiURL string, sponsorOutpoint *wire.OutPoint, anchorAddrs []string,
changeScript []byte, feeRate uint32) error {
changeAddr string, feeRate uint32) error {

signer := &lnd.Signer{
ExtendedKey: rootKey,
ChainParams: chainParams,
var (
signer = &lnd.Signer{
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
// to the block explorer, so we can fetch the witness utxo.
Expand Down Expand Up @@ -209,7 +213,6 @@ func createPullTransactionTemplate(rootKey *hdkeychain.ExtendedKey,
}

// Now we can calculate the fee and add the change output.
estimator.AddP2WKHOutput()
anchorAmt := uint64(len(anchorAddrs)) * 330
totalOutputValue := btcutil.Amount(sponsorTxOut.Value + anchorAmt)
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
Expand Down
27 changes: 6 additions & 21 deletions cmd/chantools/recoverloopin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"encoding/hex"
"fmt"

"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
Expand Down Expand Up @@ -165,29 +164,20 @@ func (c *recoverLoopInCommand) Execute(_ *cobra.Command, _ []string) error {
}

// 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 {
return err
}

// Calculate the sweep fee.
estimator := &input.TxWeightEstimator{}
err = htlc.AddTimeoutToEstimator(estimator)
err = htlc.AddTimeoutToEstimator(&estimator)
if err != nil {
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(
1000 * c.FeeRate,
).FeePerKWeight()
Expand Down Expand Up @@ -216,13 +206,8 @@ func (c *recoverLoopInCommand) Execute(_ *cobra.Command, _ []string) error {
})

// Add output for the destination address.
sweepPkScript, err := txscript.PayToAddrScript(sweepAddr)
if err != nil {
return err
}

sweepTx.AddTxOut(&wire.TxOut{
PkScript: sweepPkScript,
PkScript: sweepScript,
Value: int64(loopIn.Contract.AmountRequested) - int64(fee),
})

Expand Down
33 changes: 17 additions & 16 deletions cmd/chantools/rescuefunding.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,35 +236,38 @@ func (c *rescueFundingCommand) Execute(_ *cobra.Command, _ []string) error {
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(
localKeyDesc, remotePubKey, signer, chainOp,
sweepScript, btcutil.Amount(c.FeeRate), c.APIURL,
localKeyDesc, remotePubKey, signer, chainOp, c.SweepAddr,
btcutil.Amount(c.FeeRate), c.APIURL,
)
}

func rescueFunding(localKeyDesc *keychain.KeyDescriptor,
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 {

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.
txIn := &wire.TxIn{
PreviousOutPoint: *chainPoint,
Sequence: 0,
}
txOut := &wire.TxOut{
PkScript: sweepPKScript,
PkScript: sweepScript,
}

// Locate the output in the funding TX.
api := newExplorerAPI(apiURL)
tx, err := api.Transaction(chainPoint.Hash.String())
if err != nil {
return fmt.Errorf("error fetching UTXO info for outpoint %s: "+
Expand Down Expand Up @@ -303,17 +306,15 @@ func rescueFunding(localKeyDesc *keychain.KeyDescriptor,
WitnessScript: witnessScript,
Unknowns: []*psbt.Unknown{{
// 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.
Key: PsbtKeyTypeOutputMissingSigPubkey,
Value: remoteKey.SerializeCompressed(),
}},
}

// Estimate the transaction weight so we can do the fee estimation.
var estimator input.TxWeightEstimator
// Estimate the transaction weight, so we can do the fee estimation.
estimator.AddWitnessInput(MultiSigWitnessSize)
estimator.AddP2WKHOutput()
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
txOut.Value = utxo.Value - int64(totalFee)
Expand Down
Loading

0 comments on commit a05962e

Please sign in to comment.