@@ -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 {
3534func 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 {
0 commit comments