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