66	"encoding/hex" 
77	"encoding/json" 
88	"fmt" 
9- 	"io/ioutil" 
109	"os" 
1110	"strconv" 
1211	"strings" 
@@ -28,6 +27,8 @@ type zombieRecoveryMakeOfferCommand struct {
2827	Node2    string 
2928	FeeRate  uint32 
3029
30+ 	MatchOnly  bool 
31+ 
3132	rootKey  * rootKey 
3233	cmd      * cobra.Command 
3334}
@@ -64,6 +65,10 @@ a counter offer.`,
6465		& cc .FeeRate , "feerate" , defaultFeeSatPerVByte , "fee rate to " + 
6566			"use for the sweep transaction in sat/vByte" ,
6667	)
68+ 	cc .cmd .Flags ().BoolVar (
69+ 		& cc .MatchOnly , "matchonly" , false , "only match the keys, " + 
70+ 			"don't create an offer" ,
71+ 	)
6772
6873	cc .rootKey  =  newRootKey (cc .cmd , "signing the offer" )
6974
@@ -82,12 +87,12 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
8287		c .FeeRate  =  defaultFeeSatPerVByte 
8388	}
8489
85- 	node1Bytes , err  :=  ioutil .ReadFile (c .Node1 )
90+ 	node1Bytes , err  :=  os .ReadFile (c .Node1 )
8691	if  err  !=  nil  {
8792		return  fmt .Errorf ("error reading node1 key file %s: %w" ,
8893			c .Node1 , err )
8994	}
90- 	node2Bytes , err  :=  ioutil .ReadFile (c .Node2 )
95+ 	node2Bytes , err  :=  os .ReadFile (c .Node2 )
9196	if  err  !=  nil  {
9297		return  fmt .Errorf ("error reading node2 key file %s: %w" ,
9398			c .Node2 , err )
@@ -153,6 +158,22 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
153158		}
154159	}
155160
161+ 	// If we're only matching, we can stop here. 
162+ 	if  c .MatchOnly  {
163+ 		ourPubKeys , err  :=  parseKeys (keys1 .Node1 .MultisigKeys )
164+ 		if  err  !=  nil  {
165+ 			return  fmt .Errorf ("error parsing their keys: %w" , err )
166+ 		}
167+ 
168+ 		theirPubKeys , err  :=  parseKeys (keys2 .Node2 .MultisigKeys )
169+ 		if  err  !=  nil  {
170+ 			return  fmt .Errorf ("error parsing our keys: %w" , err )
171+ 		}
172+ 		return  matchKeys (
173+ 			keys1 .Channels , ourPubKeys , theirPubKeys , chainParams ,
174+ 		)
175+ 	}
176+ 
156177	// Make sure one of the nodes is ours. 
157178	_ , pubKey , _ , err  :=  lnd .DeriveKey (
158179		extendedKey , lnd .IdentityPath (chainParams ), chainParams ,
@@ -206,52 +227,19 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
206227		return  fmt .Errorf ("payout address missing" )
207228	}
208229
209- 	ourPubKeys  :=  make ([]* btcec.PublicKey , len (ourKeys ))
210- 	theirPubKeys  :=  make ([]* btcec.PublicKey , len (theirKeys ))
211- 	for  idx , pubKeyHex  :=  range  ourKeys  {
212- 		ourPubKeys [idx ], err  =  pubKeyFromHex (pubKeyHex )
213- 		if  err  !=  nil  {
214- 			return  fmt .Errorf ("error parsing our pubKey: %w" , err )
215- 		}
216- 	}
217- 	for  idx , pubKeyHex  :=  range  theirKeys  {
218- 		theirPubKeys [idx ], err  =  pubKeyFromHex (pubKeyHex )
219- 		if  err  !=  nil  {
220- 			return  fmt .Errorf ("error parsing their pubKey: %w" , err )
221- 		}
230+ 	ourPubKeys , err  :=  parseKeys (ourKeys )
231+ 	if  err  !=  nil  {
232+ 		return  fmt .Errorf ("error parsing their keys: %w" , err )
222233	}
223234
224- 	// Loop through all channels and all keys now, this will definitely take 
225- 	// a while. 
226- channelLoop:
227- 	for  _ , channel  :=  range  keys1 .Channels  {
228- 		for  ourKeyIndex , ourKey  :=  range  ourPubKeys  {
229- 			for  _ , theirKey  :=  range  theirPubKeys  {
230- 				match , witnessScript , err  :=  matchScript (
231- 					channel .Address , ourKey , theirKey ,
232- 					chainParams ,
233- 				)
234- 				if  err  !=  nil  {
235- 					return  fmt .Errorf ("error matching " + 
236- 						"keys to script: %w" , err )
237- 				}
238- 
239- 				if  match  {
240- 					channel .ourKeyIndex  =  uint32 (ourKeyIndex )
241- 					channel .ourKey  =  ourKey 
242- 					channel .theirKey  =  theirKey 
243- 					channel .witnessScript  =  witnessScript 
244- 
245- 					log .Infof ("Found keys for channel %s" ,
246- 						channel .ChanPoint )
247- 
248- 					continue  channelLoop
249- 				}
250- 			}
251- 		}
235+ 	theirPubKeys , err  :=  parseKeys (theirKeys )
236+ 	if  err  !=  nil  {
237+ 		return  fmt .Errorf ("error parsing our keys: %w" , err )
238+ 	}
252239
253- 		return  fmt .Errorf ("didn't find matching multisig keys for " + 
254- 			"channel %s" , channel .ChanPoint )
240+ 	err  =  matchKeys (keys1 .Channels , ourPubKeys , theirPubKeys , chainParams )
241+ 	if  err  !=  nil  {
242+ 		return  err 
255243	}
256244
257245	// Let's now sum up the tally of how much of the rescued funds should 
@@ -444,6 +432,64 @@ channelLoop:
444432	return  nil 
445433}
446434
435+ // parseKeys parses a list of string keys into public keys. 
436+ func  parseKeys (keys  []string ) ([]* btcec.PublicKey , error ) {
437+ 	pubKeys  :=  make ([]* btcec.PublicKey , 0 , len (keys ))
438+ 	for  _ , key  :=  range  keys  {
439+ 		pubKey , err  :=  pubKeyFromHex (key )
440+ 		if  err  !=  nil  {
441+ 			return  nil , err 
442+ 		}
443+ 		pubKeys  =  append (pubKeys , pubKey )
444+ 	}
445+ 
446+ 	return  pubKeys , nil 
447+ }
448+ 
449+ // matchKeys tries to match the keys from the two nodes. It updates the channels 
450+ // with the correct keys and witness scripts. 
451+ func  matchKeys (channels  []* channel , ourPubKeys , theirPubKeys  []* btcec.PublicKey ,
452+ 	chainParams  * chaincfg.Params ) error  {
453+ 
454+ 	// Loop through all channels and all keys now, this will definitely take 
455+ 	// a while. 
456+ channelLoop:
457+ 	for  _ , channel  :=  range  channels  {
458+ 		for  ourKeyIndex , ourKey  :=  range  ourPubKeys  {
459+ 			for  _ , theirKey  :=  range  theirPubKeys  {
460+ 				match , witnessScript , err  :=  matchScript (
461+ 					channel .Address , ourKey , theirKey ,
462+ 					chainParams ,
463+ 				)
464+ 				if  err  !=  nil  {
465+ 					return  fmt .Errorf ("error matching " + 
466+ 						"keys to script: %w" , err )
467+ 				}
468+ 
469+ 				if  match  {
470+ 					channel .ourKeyIndex  =  uint32 (ourKeyIndex )
471+ 					channel .ourKey  =  ourKey 
472+ 					channel .theirKey  =  theirKey 
473+ 					channel .witnessScript  =  witnessScript 
474+ 
475+ 					log .Infof ("Found keys for channel %s: " + 
476+ 						"our key %x, their key %x" ,
477+ 						channel .ChanPoint ,
478+ 						ourKey .SerializeCompressed (),
479+ 						theirKey .SerializeCompressed ())
480+ 
481+ 					continue  channelLoop
482+ 				}
483+ 			}
484+ 		}
485+ 
486+ 		return  fmt .Errorf ("didn't find matching multisig keys for " + 
487+ 			"channel %s" , channel .ChanPoint )
488+ 	}
489+ 
490+ 	return  nil 
491+ }
492+ 
447493func  matchScript (address  string , key1 , key2  * btcec.PublicKey ,
448494	params  * chaincfg.Params ) (bool , []byte , error ) {
449495
0 commit comments