Skip to content

Commit

Permalink
zombierecovery: make preparekeys CLN compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Aug 3, 2024
1 parent 221b751 commit 4694e5e
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 52 deletions.
21 changes: 5 additions & 16 deletions cmd/chantools/zombierecovery_makeoffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,22 +159,6 @@ 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 @@ -243,6 +227,11 @@ func (c *zombieRecoveryMakeOfferCommand) Execute(_ *cobra.Command,
return err
}

// If we're only matching, we can stop here.
if c.MatchOnly {
return nil
}

// Let's now sum up the tally of how much of the rescued funds should
// go to which party.
var (
Expand Down
50 changes: 32 additions & 18 deletions cmd/chantools/zombierecovery_makeoffer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,37 @@ import (
"github.com/stretchr/testify/require"
)

var (
key1Bytes, _ = hex.DecodeString(
"0201943d78d61c8ad50ba57164830f536c156d8d89d979448bef3e67f564" +
"ea0ab6",
)
key1, _ = btcec.ParsePubKey(key1Bytes)
key2Bytes, _ = hex.DecodeString(
"038b88de18064024e9da4dfc9c804283b3077a265dcd73ad3615b50badcb" +
"debd5b",
)
key2, _ = btcec.ParsePubKey(key2Bytes)
addr = "bc1qp5jnhnavt32fjwhnf5ttpvvym7e0syp79q5l9skz545q62d8u2uq05" +
"ul63"
)

func TestMatchScript(t *testing.T) {
ok, _, err := matchScript(addr, key1, key2, &chaincfg.MainNetParams)
require.NoError(t, err)
require.True(t, ok)
testCases := []struct {
key1 string
key2 string
addr string
params *chaincfg.Params
}{{
key1: "0201943d78d61c8ad50ba57164830f536c156d8d89d979448bef3e67f564ea0ab6",
key2: "038b88de18064024e9da4dfc9c804283b3077a265dcd73ad3615b50badcbdebd5b",
addr: "bc1qp5jnhnavt32fjwhnf5ttpvvym7e0syp79q5l9skz545q62d8u2uq05ul63",
params: &chaincfg.MainNetParams,
}, {
key1: "03585d8e760bd0925da67d9c22a69dcad9f51f90a39f9a681971268555975ea30d",
key2: "0326a2171c97673cc8cd7a04a043f0224c59591fc8c9de320a48f7c9b68ab0ae2b",
addr: "bcrt1qhcn39q6jc0krkh9va230y2z6q96zadt8fhxw3erv92fzlrw83cyq40nwek",
params: &chaincfg.RegressionNetParams,
}}

for _, tc := range testCases {
key1Bytes, err := hex.DecodeString(tc.key1)
require.NoError(t, err)
key1, err := btcec.ParsePubKey(key1Bytes)
require.NoError(t, err)

key2Bytes, err := hex.DecodeString(tc.key2)
require.NoError(t, err)
key2, err := btcec.ParsePubKey(key2Bytes)
require.NoError(t, err)

ok, _, err := matchScript(tc.addr, key1, key2, tc.params)
require.NoError(t, err)
require.True(t, ok)
}
}
126 changes: 108 additions & 18 deletions cmd/chantools/zombierecovery_preparekeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"os"
"time"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/lightninglabs/chantools/cln"
"github.com/lightninglabs/chantools/lnd"
"github.com/spf13/cobra"
)
Expand All @@ -23,6 +25,8 @@ type zombieRecoveryPrepareKeysCommand struct {

NumKeys uint32

HsmSecret string

rootKey *rootKey
cmd *cobra.Command
}
Expand Down Expand Up @@ -56,6 +60,12 @@ correct ones for the matched channels.`,
&cc.NumKeys, "num_keys", numMultisigKeys, "the number of "+
"multisig keys to derive",
)
cc.cmd.Flags().StringVar(
&cc.HsmSecret, "hsm_secret", "", "the hex encoded HSM secret "+
"to use for deriving the multisig keys for a CLN "+
"node; obtain by running 'xxd -p -c32 "+
"~/.lightning/bitcoin/hsm_secret'",
)

cc.rootKey = newRootKey(cc.cmd, "deriving the multisig keys")

Expand All @@ -65,12 +75,7 @@ correct ones for the matched channels.`,
func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
_ []string) error {

extendedKey, err := c.rootKey.read()
if err != nil {
return fmt.Errorf("error reading root key: %w", err)
}

_, err = lnd.GetP2WPKHScript(c.PayoutAddr, chainParams)
_, err := lnd.GetP2WPKHScript(c.PayoutAddr, chainParams)
if err != nil {
return errors.New("invalid payout address, must be P2WPKH")
}
Expand All @@ -93,19 +98,51 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
return errors.New("invalid match file, node info missing")
}

// Derive the keys for the node type, depending on the input flags.
var pubKeyStr string
switch {
case c.HsmSecret != "":
pubKeyStr, err = c.clnDeriveKeys(match)
default:
pubKeyStr, err = c.lndDeriveKeys(match)
}
if err != nil {
return err
}

// Write the result back into a new file.
matchBytes, err := json.MarshalIndent(match, "", " ")
if err != nil {
return err
}

fileName := fmt.Sprintf("results/preparedkeys-%s-%s.json",
time.Now().Format("2006-01-02"), pubKeyStr)
log.Infof("Writing result to %s", fileName)
return os.WriteFile(fileName, matchBytes, 0644)
}

func (c *zombieRecoveryPrepareKeysCommand) lndDeriveKeys(match *match) (string,
error) {

extendedKey, err := c.rootKey.read()
if err != nil {
return "", fmt.Errorf("error reading root key: %w", err)
}

_, pubKey, _, err := lnd.DeriveKey(
extendedKey, lnd.IdentityPath(chainParams), chainParams,
)
if err != nil {
return fmt.Errorf("error deriving identity pubkey: %w", err)
return "", fmt.Errorf("error deriving identity pubkey: %w", err)
}

pubKeyStr := hex.EncodeToString(pubKey.SerializeCompressed())
var nodeInfo *nodeInfo
switch {
case match.Node1.PubKey != pubKeyStr && match.Node2.PubKey != pubKeyStr:
return fmt.Errorf("derived pubkey %s from seed but that key "+
"was not found in the match file %s", pubKeyStr,
return "", fmt.Errorf("derived pubkey %s from seed but that "+
"key was not found in the match file %s", pubKeyStr,
c.MatchFile)

case match.Node1.PubKey == pubKeyStr:
Expand All @@ -122,8 +159,8 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
chainParams,
)
if err != nil {
return fmt.Errorf("error deriving multisig pubkey: %w",
err)
return "", fmt.Errorf("error deriving multisig "+
"pubkey: %w", err)
}

nodeInfo.MultisigKeys = append(
Expand All @@ -133,14 +170,67 @@ func (c *zombieRecoveryPrepareKeysCommand) Execute(_ *cobra.Command,
}
nodeInfo.PayoutAddr = c.PayoutAddr

// Write the result back into a new file.
matchBytes, err := json.MarshalIndent(match, "", " ")
return pubKeyStr, nil
}

func (c *zombieRecoveryPrepareKeysCommand) clnDeriveKeys(match *match) (string,
error) {

secretBytes, err := hex.DecodeString(c.HsmSecret)
if err != nil {
return err
return "", fmt.Errorf("error decoding HSM secret: %w", err)
}

fileName := fmt.Sprintf("results/preparedkeys-%s-%s.json",
time.Now().Format("2006-01-02"), pubKeyStr)
log.Infof("Writing result to %s", fileName)
return os.WriteFile(fileName, matchBytes, 0644)
var hsmSecret [32]byte
copy(hsmSecret[:], secretBytes)

nodePubKey, err := cln.NodeKey(hsmSecret)
if err != nil {
return "", fmt.Errorf("error deriving node pubkey: %w", err)
}

pubKeyStr := hex.EncodeToString(nodePubKey.SerializeCompressed())
var ourNodeInfo, theirNodeInfo *nodeInfo
switch {
case match.Node1.PubKey != pubKeyStr && match.Node2.PubKey != pubKeyStr:
return "", fmt.Errorf("derived pubkey %s from seed but that "+
"key was not found in the match file %s", pubKeyStr,
c.MatchFile)

case match.Node1.PubKey == pubKeyStr:
ourNodeInfo = match.Node1
theirNodeInfo = match.Node2

default:
ourNodeInfo = match.Node2
theirNodeInfo = match.Node1
}

theirNodeKeyBytes, err := hex.DecodeString(theirNodeInfo.PubKey)
if err != nil {
return "", fmt.Errorf("error decoding peer pubkey: %w", err)
}
theirNodeKey, err := btcec.ParsePubKey(theirNodeKeyBytes)
if err != nil {
return "", fmt.Errorf("error parsing peer pubkey: %w", err)
}

// Derive all 2500 keys now, this might take a while.
for index := range c.NumKeys {
pubKey, err := cln.FundingKey(
hsmSecret, theirNodeKey, uint64(index),
)
if err != nil {
return "", fmt.Errorf("error deriving multisig "+
"pubkey: %w", err)
}

ourNodeInfo.MultisigKeys = append(
ourNodeInfo.MultisigKeys,
hex.EncodeToString(pubKey.SerializeCompressed()),
)
}
ourNodeInfo.PayoutAddr = c.PayoutAddr

return pubKeyStr, nil
}

0 comments on commit 4694e5e

Please sign in to comment.