@@ -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