Merge pull request #117 from lightninglabs/zombie-matching

zombierecovery: add --matchonly flag to makeoffer, --numkeys to preparekeys
pull/118/head v0.12.2
Oliver Gugger 3 months ago committed by GitHub
commit d5d5a91430
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -30,7 +30,7 @@ Example (make sure you always use the latest version!):
```shell
$ cd /tmp
$ wget -O chantools.tar.gz https://github.com/lightninglabs/chantools/releases/download/v0.12.0/chantools-linux-amd64-v0.12.0.tar.gz
$ wget -O chantools.tar.gz https://github.com/lightninglabs/chantools/releases/download/v0.12.2/chantools-linux-amd64-v0.12.2.tar.gz
$ tar -zxvf chantools.tar.gz
$ sudo mv chantools-*/chantools /usr/local/bin/
```

@ -33,7 +33,7 @@ const (
// version is the current version of the tool. It is set during build.
// NOTE: When changing this, please also update the version in the
// download link shown in the README.
version = "0.12.1"
version = "0.12.2"
na = "n/a"
// lndVersion is the current version of lnd that we support. This is

@ -6,7 +6,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
@ -28,6 +27,8 @@ type zombieRecoveryMakeOfferCommand struct {
Node2 string
FeeRate uint32
MatchOnly bool
rootKey *rootKey
cmd *cobra.Command
}
@ -64,6 +65,10 @@ a counter offer.`,
&cc.FeeRate, "feerate", defaultFeeSatPerVByte, "fee rate to "+
"use for the sweep transaction in sat/vByte",
)
cc.cmd.Flags().BoolVar(
&cc.MatchOnly, "matchonly", false, "only match the keys, "+
"don't create an offer",
)
cc.rootKey = newRootKey(cc.cmd, "signing the offer")
@ -82,12 +87,12 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
c.FeeRate = defaultFeeSatPerVByte
}
node1Bytes, err := ioutil.ReadFile(c.Node1)
node1Bytes, err := os.ReadFile(c.Node1)
if err != nil {
return fmt.Errorf("error reading node1 key file %s: %w",
c.Node1, err)
}
node2Bytes, err := ioutil.ReadFile(c.Node2)
node2Bytes, err := os.ReadFile(c.Node2)
if err != nil {
return fmt.Errorf("error reading node2 key file %s: %w",
c.Node2, err)
@ -153,6 +158,22 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
}
}
// If we're only matching, we can stop here.
if c.MatchOnly {
ourPubKeys, err := parseKeys(keys1.Node1.MultisigKeys)
if err != nil {
return fmt.Errorf("error parsing their keys: %w", err)
}
theirPubKeys, err := parseKeys(keys2.Node2.MultisigKeys)
if err != nil {
return fmt.Errorf("error parsing our keys: %w", err)
}
return matchKeys(
keys1.Channels, ourPubKeys, theirPubKeys, chainParams,
)
}
// Make sure one of the nodes is ours.
_, pubKey, _, err := lnd.DeriveKey(
extendedKey, lnd.IdentityPath(chainParams), chainParams,
@ -206,52 +227,19 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
return fmt.Errorf("payout address missing")
}
ourPubKeys := make([]*btcec.PublicKey, len(ourKeys))
theirPubKeys := make([]*btcec.PublicKey, len(theirKeys))
for idx, pubKeyHex := range ourKeys {
ourPubKeys[idx], err = pubKeyFromHex(pubKeyHex)
if err != nil {
return fmt.Errorf("error parsing our pubKey: %w", err)
}
}
for idx, pubKeyHex := range theirKeys {
theirPubKeys[idx], err = pubKeyFromHex(pubKeyHex)
if err != nil {
return fmt.Errorf("error parsing their pubKey: %w", err)
}
ourPubKeys, err := parseKeys(ourKeys)
if err != nil {
return fmt.Errorf("error parsing their keys: %w", err)
}
// Loop through all channels and all keys now, this will definitely take
// a while.
channelLoop:
for _, channel := range keys1.Channels {
for ourKeyIndex, ourKey := range ourPubKeys {
for _, theirKey := range theirPubKeys {
match, witnessScript, err := matchScript(
channel.Address, ourKey, theirKey,
chainParams,
)
if err != nil {
return fmt.Errorf("error matching "+
"keys to script: %w", err)
}
if match {
channel.ourKeyIndex = uint32(ourKeyIndex)
channel.ourKey = ourKey
channel.theirKey = theirKey
channel.witnessScript = witnessScript
log.Infof("Found keys for channel %s",
channel.ChanPoint)
continue channelLoop
}
}
}
theirPubKeys, err := parseKeys(theirKeys)
if err != nil {
return fmt.Errorf("error parsing our keys: %w", err)
}
return fmt.Errorf("didn't find matching multisig keys for "+
"channel %s", channel.ChanPoint)
err = matchKeys(keys1.Channels, ourPubKeys, theirPubKeys, chainParams)
if err != nil {
return err
}
// Let's now sum up the tally of how much of the rescued funds should
@ -444,6 +432,64 @@ channelLoop:
return nil
}
// parseKeys parses a list of string keys into public keys.
func parseKeys(keys []string) ([]*btcec.PublicKey, error) {
pubKeys := make([]*btcec.PublicKey, 0, len(keys))
for _, key := range keys {
pubKey, err := pubKeyFromHex(key)
if err != nil {
return nil, err
}
pubKeys = append(pubKeys, pubKey)
}
return pubKeys, nil
}
// matchKeys tries to match the keys from the two nodes. It updates the channels
// with the correct keys and witness scripts.
func matchKeys(channels []*channel, ourPubKeys, theirPubKeys []*btcec.PublicKey,
chainParams *chaincfg.Params) error {
// Loop through all channels and all keys now, this will definitely take
// a while.
channelLoop:
for _, channel := range channels {
for ourKeyIndex, ourKey := range ourPubKeys {
for _, theirKey := range theirPubKeys {
match, witnessScript, err := matchScript(
channel.Address, ourKey, theirKey,
chainParams,
)
if err != nil {
return fmt.Errorf("error matching "+
"keys to script: %w", err)
}
if match {
channel.ourKeyIndex = uint32(ourKeyIndex)
channel.ourKey = ourKey
channel.theirKey = theirKey
channel.witnessScript = witnessScript
log.Infof("Found keys for channel %s: "+
"our key %x, their key %x",
channel.ChanPoint,
ourKey.SerializeCompressed(),
theirKey.SerializeCompressed())
continue channelLoop
}
}
}
return fmt.Errorf("didn't find matching multisig keys for "+
"channel %s", channel.ChanPoint)
}
return nil
}
func matchScript(address string, key1, key2 *btcec.PublicKey,
params *chaincfg.Params) (bool, []byte, error) {

@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/lightninglabs/chantools/lnd"
@ -20,6 +21,8 @@ type zombieRecoveryPrepareKeysCommand struct {
MatchFile string
PayoutAddr string
NumKeys uint32
rootKey *rootKey
cmd *cobra.Command
}
@ -47,7 +50,12 @@ correct ones for the matched channels.`,
cc.cmd.Flags().StringVar(
&cc.PayoutAddr, "payout_addr", "", "the address where this "+
"node's rescued funds should be sent to, must be a "+
"P2WPKH (native SegWit) address")
"P2WPKH (native SegWit) address",
)
cc.cmd.Flags().Uint32Var(
&cc.NumKeys, "num_keys", numMultisigKeys, "the number of "+
"multisig keys to derive",
)
cc.rootKey = newRootKey(cc.cmd, "deriving the multisig keys")
@ -108,9 +116,9 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
}
// Derive all 2500 keys now, this might take a while.
for index := 0; index < numMultisigKeys; index++ {
for index := uint32(0); index < c.NumKeys; index++ {
_, pubKey, _, err := lnd.DeriveKey(
extendedKey, lnd.MultisigPath(chainParams, index),
extendedKey, lnd.MultisigPath(chainParams, int(index)),
chainParams,
)
if err != nil {
@ -134,5 +142,5 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
fileName := fmt.Sprintf("results/preparedkeys-%s-%s.json",
time.Now().Format("2006-01-02"), pubKeyStr)
log.Infof("Writing result to %s", fileName)
return ioutil.WriteFile(fileName, matchBytes, 0644)
return os.WriteFile(fileName, matchBytes, 0644)
}

Loading…
Cancel
Save