Skip to content

Commit 99724b6

Browse files
committed
multi: make derivekey CLN compatible
1 parent 168d1cb commit 99724b6

File tree

4 files changed

+90
-19
lines changed

4 files changed

+90
-19
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ Legend:
484484
| [compactdb](doc/chantools_compactdb.md) | Run database compaction manually to reclaim space |
485485
| [createwallet](doc/chantools_createwallet.md) | :pencil: Create a new lnd compatible wallet.db file from an existing seed or by generating a new one |
486486
| [deletepayments](doc/chantools_deletepayments.md) | Remove ALL payments from a `channel.db` file to reduce size |
487-
| [derivekey](doc/chantools_derivekey.md) | :pencil: Derive a single private/public key from `lnd`'s seed, use to test seed |
487+
| [derivekey](doc/chantools_derivekey.md) | :pencil: (**CLN**) Derive a single private/public key from `lnd`'s seed, use to test seed |
488488
| [doublespendinputs](doc/chantools_doublespendinputs.md) | :pencil: Tries to double spend the given inputs by deriving the private for the address and sweeping the funds to the given address |
489489
| [dropchannelgraph](doc/chantools_dropchannelgraph.md) | ( :warning: ) Completely drop the channel graph from a `channel.db` to force re-sync (not recommended while channels are open!) |
490490
| [dropgraphzombies](doc/chantools_dropgraphzombies.md) | Drop all zombie channels from a `channel.db` to force a graph re-sync |

cmd/chantools/derivekey.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package main
22

33
import (
4+
"encoding/hex"
5+
"errors"
46
"fmt"
57

68
"github.com/btcsuite/btcd/btcutil"
79
"github.com/btcsuite/btcd/btcutil/hdkeychain"
10+
"github.com/lightninglabs/chantools/cln"
811
"github.com/lightninglabs/chantools/lnd"
912
"github.com/spf13/cobra"
1013
)
@@ -27,6 +30,8 @@ type deriveKeyCommand struct {
2730
Neuter bool
2831
Identity bool
2932

33+
HsmSecret string
34+
3035
rootKey *rootKey
3136
cmd *cobra.Command
3237
}
@@ -53,8 +58,14 @@ chantools derivekey --identity`,
5358
"only public key(s)",
5459
)
5560
cc.cmd.Flags().BoolVar(
56-
&cc.Identity, "identity", false, "derive the lnd "+
57-
"identity_pubkey",
61+
&cc.Identity, "identity", false, "derive the node's identity "+
62+
"public key",
63+
)
64+
cc.cmd.Flags().StringVar(
65+
&cc.HsmSecret, "hsm_secret", "", "the hex encoded HSM secret "+
66+
"to use for deriving the multisig keys for a CLN "+
67+
"node; obtain by running 'xxd -p -c32 "+
68+
"~/.lightning/bitcoin/hsm_secret'",
5869
)
5970

6071
cc.rootKey = newRootKey(cc.cmd, "decrypting the backup")
@@ -63,6 +74,40 @@ chantools derivekey --identity`,
6374
}
6475

6576
func (c *deriveKeyCommand) Execute(_ *cobra.Command, _ []string) error {
77+
if c.HsmSecret != "" {
78+
if c.Path != "" {
79+
return errors.New("cannot specify --path with " +
80+
"--hsm_secret, only identity key can be " +
81+
"derived")
82+
}
83+
84+
secretBytes, err := hex.DecodeString(c.HsmSecret)
85+
if err != nil {
86+
return fmt.Errorf("error decoding HSM secret: %w", err)
87+
}
88+
89+
var hsmSecret [32]byte
90+
copy(hsmSecret[:], secretBytes)
91+
92+
nodePubKey, _, err := cln.NodeKey(hsmSecret)
93+
if err != nil {
94+
return fmt.Errorf("error deriving node key from HSM: "+
95+
"%w", err)
96+
}
97+
98+
result := fmt.Sprintf(
99+
"Node identity public key: %x",
100+
nodePubKey.SerializeCompressed(),
101+
)
102+
fmt.Println(result)
103+
104+
// For the tests, also log as trace level which is disabled by
105+
// default.
106+
log.Tracef(result)
107+
108+
return nil
109+
}
110+
66111
extendedKey, err := c.rootKey.read()
67112
if err != nil {
68113
return fmt.Errorf("error reading root key: %w", err)

cmd/chantools/derivekey_test.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ func TestDeriveKeyXprv(t *testing.T) {
9999
err := derive.Execute(nil, nil)
100100
require.NoError(t, err)
101101

102-
h.assertLogContains("cQcdieZy2d1TAdCsa5MjmHJs2gdHcD7x22nDbhJyVTUa3Ax" +
103-
"5KB3w")
102+
h.assertLogContains(
103+
"cQcdieZy2d1TAdCsa5MjmHJs2gdHcD7x22nDbhJyVTUa3Ax5KB3w",
104+
)
104105
}
105106

106107
func TestDeriveKeyXpub(t *testing.T) {
@@ -120,8 +121,10 @@ func TestDeriveKeyXpub(t *testing.T) {
120121
err := derive.Execute(nil, nil)
121122
require.NoError(t, err)
122123

123-
h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" +
124-
"05eb1ab99aa05e01c2a")
124+
h.assertLogContains(
125+
"03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b04257105eb1ab99aa05e" +
126+
"01c2a",
127+
)
125128
}
126129

127130
func TestDeriveKeyXpubNoNeuter(t *testing.T) {
@@ -140,6 +143,27 @@ func TestDeriveKeyXpubNoNeuter(t *testing.T) {
140143
err := derive.Execute(nil, nil)
141144
require.NoError(t, err)
142145

143-
h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" +
144-
"05eb1ab99aa05e01c2a")
146+
h.assertLogContains(
147+
"03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b04257105eb1ab99aa05e" +
148+
"01c2a",
149+
)
150+
}
151+
152+
func TestDeriveKeyHsmSecret(t *testing.T) {
153+
h := newHarness(t)
154+
155+
// Derive a specific key from the serialized root key.
156+
derive := &deriveKeyCommand{
157+
Identity: true,
158+
HsmSecret: "471a115fb8edd6281f883ac82509fb0f30707e590d895f683" +
159+
"2d781f1639c07ff",
160+
}
161+
162+
err := derive.Execute(nil, nil)
163+
require.NoError(t, err)
164+
165+
h.assertLogContains(
166+
"03508beb59d2ec4772cd7b143bbef0fdac204b240747e82bc5fe58bd0418" +
167+
"4f35a3",
168+
)
145169
}

doc/chantools_derivekey.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,23 @@ chantools derivekey --identity
2323
### Options
2424

2525
```
26-
--bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag
27-
-h, --help help for derivekey
28-
--identity derive the lnd identity_pubkey
29-
--neuter don't output private key(s), only public key(s)
30-
--path string BIP32 derivation path to derive; must start with "m/"
31-
--rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed
32-
--walletdb string read the seed/master root key to use for decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag
26+
--bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag
27+
-h, --help help for derivekey
28+
--hsm_secret string 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'
29+
--identity derive the node's identity public key
30+
--neuter don't output private key(s), only public key(s)
31+
--path string BIP32 derivation path to derive; must start with "m/"
32+
--rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed
33+
--walletdb string read the seed/master root key to use for decrypting the backup from an lnd wallet.db file instead of asking for a seed or providing the --rootkey flag
3334
```
3435

3536
### Options inherited from parent commands
3637

3738
```
38-
-r, --regtest Indicates if regtest parameters should be used
39-
-s, --signet Indicates if the public signet parameters should be used
40-
-t, --testnet Indicates if testnet parameters should be used
39+
--nologfile If set, no log file will be created. This is useful for testing purposes where we don't want to create a log file.
40+
-r, --regtest Indicates if regtest parameters should be used
41+
-s, --signet Indicates if the public signet parameters should be used
42+
-t, --testnet Indicates if testnet parameters should be used
4143
```
4244

4345
### SEE ALSO

0 commit comments

Comments
 (0)