Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zombierecovery: add --matchonly flag to makeoffer, --numkeys to preparekeys #117

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/
```
Expand Down
2 changes: 1 addition & 1 deletion cmd/chantools/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
138 changes: 92 additions & 46 deletions cmd/chantools/zombierecovery_makeoffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
Expand All @@ -28,6 +27,8 @@ type zombieRecoveryMakeOfferCommand struct {
Node2 string
FeeRate uint32

MatchOnly bool

rootKey *rootKey
cmd *cobra.Command
}
Expand Down Expand Up @@ -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")

Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {

Expand Down
16 changes: 12 additions & 4 deletions cmd/chantools/zombierecovery_preparekeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"

"github.com/lightninglabs/chantools/lnd"
Expand All @@ -20,6 +21,8 @@ type zombieRecoveryPrepareKeysCommand struct {
MatchFile string
PayoutAddr string

NumKeys uint32

rootKey *rootKey
cmd *cobra.Command
}
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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 {
Expand All @@ -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