Skip to content

Commit 399a23a

Browse files
authored
Merge pull request #107 from lightninglabs/cli-cleanup
Support P2TR as sweep/change address everywhere
2 parents 858995a + a05962e commit 399a23a

25 files changed

+438
-209
lines changed

btc/explorer_api.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,34 +196,47 @@ func (a *ExplorerAPI) PublishTx(rawTxHex string) (string, error) {
196196
url := fmt.Sprintf("%s/tx", a.BaseURL)
197197
resp, err := http.Post(url, "text/plain", strings.NewReader(rawTxHex))
198198
if err != nil {
199-
return "", err
199+
return "", fmt.Errorf("error posting data to API '%s', "+
200+
"server might be experiencing temporary issues, try "+
201+
"again later; error details: %w", url, err)
200202
}
201203
defer resp.Body.Close()
202204
body := new(bytes.Buffer)
203205
_, err = body.ReadFrom(resp.Body)
204206
if err != nil {
205-
return "", err
207+
return "", fmt.Errorf("error fetching data from API '%s', "+
208+
"server might be experiencing temporary issues, try "+
209+
"again later; error details: %w", url, err)
206210
}
207211
return body.String(), nil
208212
}
209213

210214
func fetchJSON(url string, target interface{}) error {
211215
resp, err := http.Get(url)
212216
if err != nil {
213-
return err
217+
return fmt.Errorf("error fetching data from API '%s', "+
218+
"server might be experiencing temporary issues, try "+
219+
"again later; error details: %w", url, err)
214220
}
215221
defer resp.Body.Close()
216222

217223
body := new(bytes.Buffer)
218224
_, err = body.ReadFrom(resp.Body)
219225
if err != nil {
220-
return err
226+
return fmt.Errorf("error fetching data from API '%s', "+
227+
"server might be experiencing temporary issues, try "+
228+
"again later; error details: %w", url, err)
221229
}
222230
err = json.Unmarshal(body.Bytes(), target)
223231
if err != nil {
224232
if body.String() == "Transaction not found" {
225233
return ErrTxNotFound
226234
}
235+
236+
return fmt.Errorf("error decoding data from API '%s', "+
237+
"server might be experiencing temporary issues, try "+
238+
"again later; error details: %w", url, err)
227239
}
228-
return err
240+
241+
return nil
229242
}

btc/summary.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77
"github.com/lightninglabs/chantools/dataformat"
88
)
99

10-
func SummarizeChannels(apiURL string, channels []*dataformat.SummaryEntry,
10+
func SummarizeChannels(api *ExplorerAPI, channels []*dataformat.SummaryEntry,
1111
log btclog.Logger) (*dataformat.SummaryEntryFile, error) {
1212

1313
summaryFile := &dataformat.SummaryEntryFile{
1414
Channels: channels,
1515
}
16-
api := &ExplorerAPI{BaseURL: apiURL}
1716

1817
for idx, channel := range channels {
1918
tx, err := api.Transaction(channel.FundingTXID)

cmd/chantools/closepoolaccount.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"github.com/btcsuite/btcd/btcutil/hdkeychain"
1111
"github.com/btcsuite/btcd/txscript"
1212
"github.com/btcsuite/btcd/wire"
13-
"github.com/lightninglabs/chantools/btc"
1413
"github.com/lightninglabs/chantools/lnd"
1514
"github.com/lightninglabs/pool/account"
1615
"github.com/lightninglabs/pool/poolscript"
@@ -89,7 +88,9 @@ obtained by running 'pool accounts list' `,
8988
"API instead of just printing the TX",
9089
)
9190
cc.cmd.Flags().StringVar(
92-
&cc.SweepAddr, "sweepaddr", "", "address to sweep the funds to",
91+
&cc.SweepAddr, "sweepaddr", "", "address to recover the funds "+
92+
"to; specify '"+lnd.AddressDeriveFromWallet+"' to "+
93+
"derive a new address from the seed automatically",
9394
)
9495
cc.cmd.Flags().Uint32Var(
9596
&cc.FeeRate, "feerate", defaultFeeSatPerVByte, "fee rate to "+
@@ -125,8 +126,12 @@ func (c *closePoolAccountCommand) Execute(_ *cobra.Command, _ []string) error {
125126
}
126127

127128
// Make sure sweep addr is set.
128-
if c.SweepAddr == "" {
129-
return fmt.Errorf("sweep addr is required")
129+
err = lnd.CheckAddress(
130+
c.SweepAddr, chainParams, true, "sweep", lnd.AddrTypeP2WKH,
131+
lnd.AddrTypeP2TR,
132+
)
133+
if err != nil {
134+
return err
130135
}
131136

132137
// Parse account outpoint and auctioneer key.
@@ -161,11 +166,21 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
161166
sweepAddr string, publish bool, feeRate uint32, minExpiry,
162167
maxNumBlocks, maxNumAccounts, maxNumBatchKeys uint32) error {
163168

164-
signer := &lnd.Signer{
165-
ExtendedKey: extendedKey,
166-
ChainParams: chainParams,
169+
var (
170+
estimator input.TxWeightEstimator
171+
signer = &lnd.Signer{
172+
ExtendedKey: extendedKey,
173+
ChainParams: chainParams,
174+
}
175+
api = newExplorerAPI(apiURL)
176+
)
177+
178+
sweepScript, err := lnd.PrepareWalletAddress(
179+
sweepAddr, chainParams, &estimator, extendedKey, "sweep",
180+
)
181+
if err != nil {
182+
return err
167183
}
168-
api := &btc.ExplorerAPI{BaseURL: apiURL}
169184

170185
tx, err := api.Transaction(outpoint.Hash.String())
171186
if err != nil {
@@ -241,7 +256,6 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
241256
// Calculate the fee based on the given fee rate and our weight
242257
// estimation.
243258
var (
244-
estimator input.TxWeightEstimator
245259
prevOutFetcher = txscript.NewCannedPrevOutputFetcher(
246260
pkScript, sweepValue,
247261
)
@@ -277,15 +291,10 @@ func closePoolAccount(extendedKey *hdkeychain.ExtendedKey, apiURL string,
277291
signDesc.HashType = txscript.SigHashDefault
278292
signDesc.SignMethod = input.TaprootScriptSpendSignMethod
279293
}
280-
estimator.AddP2WKHOutput()
281294
feeRateKWeight := chainfee.SatPerKVByte(1000 * feeRate).FeePerKWeight()
282295
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
283296

284297
// Add our sweep destination output.
285-
sweepScript, err := lnd.GetP2WPKHScript(sweepAddr, chainParams)
286-
if err != nil {
287-
return err
288-
}
289298
sweepTx.TxOut = []*wire.TxOut{{
290299
Value: sweepValue - int64(totalFee),
291300
PkScript: sweepScript,

cmd/chantools/doublespendinputs.go

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/btcsuite/btcd/txscript"
1414
"github.com/btcsuite/btcd/wire"
1515
"github.com/decred/dcrd/dcrec/secp256k1/v4"
16-
"github.com/lightninglabs/chantools/btc"
1716
"github.com/lightninglabs/chantools/lnd"
1817
"github.com/lightningnetwork/lnd/input"
1918
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
@@ -35,16 +34,16 @@ type doubleSpendInputs struct {
3534
func newDoubleSpendInputsCommand() *cobra.Command {
3635
cc := &doubleSpendInputs{}
3736
cc.cmd = &cobra.Command{
38-
Use: "doublespendinputs",
39-
Short: "Tries to double spend the given inputs by deriving the " +
40-
"private for the address and sweeping the funds to the given " +
41-
"address. This can only be used with inputs that belong to " +
42-
"an lnd wallet.",
37+
Use: "doublespendinputs",
38+
Short: "Replace a transaction by double spending its input",
39+
Long: `Tries to double spend the given inputs by deriving the
40+
private for the address and sweeping the funds to the given address. This can
41+
only be used with inputs that belong to an lnd wallet.`,
4342
Example: `chantools doublespendinputs \
44-
--inputoutpoints xxxxxxxxx:y,xxxxxxxxx:y \
45-
--sweepaddr bc1q..... \
46-
--feerate 10 \
47-
--publish`,
43+
--inputoutpoints xxxxxxxxx:y,xxxxxxxxx:y \
44+
--sweepaddr bc1q..... \
45+
--feerate 10 \
46+
--publish`,
4847
RunE: cc.Execute,
4948
}
5049
cc.cmd.Flags().StringVar(
@@ -56,7 +55,9 @@ func newDoubleSpendInputsCommand() *cobra.Command {
5655
"list of outpoints to double spend in the format txid:vout",
5756
)
5857
cc.cmd.Flags().StringVar(
59-
&cc.SweepAddr, "sweepaddr", "", "address to sweep the funds to",
58+
&cc.SweepAddr, "sweepaddr", "", "address to recover the funds "+
59+
"to; specify '"+lnd.AddressDeriveFromWallet+"' to "+
60+
"derive a new address from the seed automatically",
6061
)
6162
cc.cmd.Flags().Uint32Var(
6263
&cc.FeeRate, "feerate", defaultFeeSatPerVByte, "fee rate to "+
@@ -84,24 +85,28 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
8485
}
8586

8687
// Make sure sweep addr is set.
87-
if c.SweepAddr == "" {
88-
return fmt.Errorf("sweep addr is required")
88+
err = lnd.CheckAddress(
89+
c.SweepAddr, chainParams, true, "sweep", lnd.AddrTypeP2WKH,
90+
lnd.AddrTypeP2TR,
91+
)
92+
if err != nil {
93+
return err
8994
}
9095

9196
// Make sure we have at least one input.
9297
if len(c.InputOutpoints) == 0 {
9398
return fmt.Errorf("inputoutpoints are required")
9499
}
95100

96-
api := &btc.ExplorerAPI{BaseURL: c.APIURL}
101+
api := newExplorerAPI(c.APIURL)
97102

98103
addresses := make([]btcutil.Address, 0, len(c.InputOutpoints))
99104
outpoints := make([]*wire.OutPoint, 0, len(c.InputOutpoints))
100105
privKeys := make([]*secp256k1.PrivateKey, 0, len(c.InputOutpoints))
101106

102107
// Get the addresses for the inputs.
103-
for _, input := range c.InputOutpoints {
104-
addrString, err := api.Address(input)
108+
for _, inputOutpoint := range c.InputOutpoints {
109+
addrString, err := api.Address(inputOutpoint)
105110
if err != nil {
106111
return err
107112
}
@@ -113,12 +118,12 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
113118

114119
addresses = append(addresses, addr)
115120

116-
txHash, err := chainhash.NewHashFromStr(input[:64])
121+
txHash, err := chainhash.NewHashFromStr(inputOutpoint[:64])
117122
if err != nil {
118123
return err
119124
}
120125

121-
vout, err := strconv.Atoi(input[65:])
126+
vout, err := strconv.Atoi(inputOutpoint[65:])
122127
if err != nil {
123128
return err
124129
}
@@ -139,7 +144,13 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
139144
}
140145

141146
// Start with the txweight estimator.
142-
estimator := input.TxWeightEstimator{}
147+
var estimator input.TxWeightEstimator
148+
sweepScript, err := lnd.PrepareWalletAddress(
149+
c.SweepAddr, chainParams, &estimator, extendedKey, "sweep",
150+
)
151+
if err != nil {
152+
return err
153+
}
143154

144155
// Find the key for the given addresses and add their
145156
// output weight to the tx estimator.
@@ -164,7 +175,9 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
164175
return err
165176
}
166177

167-
estimator.AddTaprootKeySpendInput(txscript.SigHashDefault)
178+
estimator.AddTaprootKeySpendInput(
179+
txscript.SigHashDefault,
180+
)
168181

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

185198
// Next get the full value of the inputs.
186199
var totalInput btcutil.Amount
187-
for _, input := range outpoints {
200+
for _, outpoint := range outpoints {
188201
// Get the transaction.
189-
tx, err := api.Transaction(input.Hash.String())
202+
tx, err := api.Transaction(outpoint.Hash.String())
190203
if err != nil {
191204
return err
192205
}
193206

194-
value := tx.Vout[input.Index].Value
207+
value := tx.Vout[outpoint.Index].Value
195208

196209
// Get the output index.
197210
totalInput += btcutil.Amount(value)
198211

199-
scriptPubkey, err := hex.DecodeString(tx.Vout[input.Index].ScriptPubkey)
212+
scriptPubkey, err := hex.DecodeString(
213+
tx.Vout[outpoint.Index].ScriptPubkey,
214+
)
200215
if err != nil {
201216
return err
202217
}
203218

204219
// Add the output to the map.
205-
prevOuts[*input] = &wire.TxOut{
220+
prevOuts[*outpoint] = &wire.TxOut{
206221
Value: int64(value),
207222
PkScript: scriptPubkey,
208223
}
209224
}
210225

211-
// Calculate the fee.
212-
sweepAddr, err := btcutil.DecodeAddress(c.SweepAddr, chainParams)
213-
if err != nil {
214-
return err
215-
}
216-
217-
switch sweepAddr.(type) {
218-
case *btcutil.AddressWitnessPubKeyHash:
219-
estimator.AddP2WKHOutput()
220-
221-
case *btcutil.AddressTaproot:
222-
estimator.AddP2TROutput()
223-
224-
default:
225-
return fmt.Errorf("address type %T not supported", sweepAddr)
226-
}
227-
228226
// Calculate the fee.
229227
feeRateKWeight := chainfee.SatPerKVByte(1000 * c.FeeRate).FeePerKWeight()
230228
totalFee := feeRateKWeight.FeeForWeight(int64(estimator.Weight()))
@@ -233,14 +231,8 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
233231
tx := wire.NewMsgTx(2)
234232

235233
// Add the inputs.
236-
for _, input := range outpoints {
237-
tx.AddTxIn(wire.NewTxIn(input, nil, nil))
238-
}
239-
240-
// Add the output.
241-
sweepScript, err := txscript.PayToAddrScript(sweepAddr)
242-
if err != nil {
243-
return err
234+
for _, outpoint := range outpoints {
235+
tx.AddTxIn(wire.NewTxIn(outpoint, nil, nil))
244236
}
245237

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

282274
default:
283-
return fmt.Errorf("address type %T not supported", addresses[i])
275+
return fmt.Errorf("address type %T not supported",
276+
addresses[i])
284277
}
285278
}
286279

@@ -291,7 +284,7 @@ func (c *doubleSpendInputs) Execute(_ *cobra.Command, _ []string) error {
291284
}
292285

293286
// Print the transaction.
294-
fmt.Printf("Sweeping transaction:\n%s\n", hex.EncodeToString(txBuf.Bytes()))
287+
fmt.Printf("Sweeping transaction:\n%x\n", txBuf.Bytes())
295288

296289
// Publish the transaction.
297290
if c.Publish {

cmd/chantools/forceclose.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111

1212
"github.com/btcsuite/btcd/btcutil/hdkeychain"
1313
"github.com/btcsuite/btcd/txscript"
14-
"github.com/lightninglabs/chantools/btc"
1514
"github.com/lightninglabs/chantools/dataformat"
1615
"github.com/lightninglabs/chantools/lnd"
1716
"github.com/lightningnetwork/lnd/channeldb"
@@ -105,7 +104,7 @@ func forceCloseChannels(apiURL string, extendedKey *hdkeychain.ExtendedKey,
105104
if err != nil {
106105
return err
107106
}
108-
api := &btc.ExplorerAPI{BaseURL: apiURL}
107+
api := newExplorerAPI(apiURL)
109108
signer := &lnd.Signer{
110109
ExtendedKey: extendedKey,
111110
ChainParams: chainParams,

0 commit comments

Comments
 (0)