diff --git a/README.md b/README.md index 4a32699..16d70a2 100644 --- a/README.md +++ b/README.md @@ -484,7 +484,7 @@ Legend: | [compactdb](doc/chantools_compactdb.md) | Run database compaction manually to reclaim space | | [createwallet](doc/chantools_createwallet.md) | :pencil: Create a new lnd compatible wallet.db file from an existing seed or by generating a new one | | [deletepayments](doc/chantools_deletepayments.md) | Remove ALL payments from a `channel.db` file to reduce size | -| [derivekey](doc/chantools_derivekey.md) | :pencil: Derive a single private/public key from `lnd`'s seed, use to test seed | +| [derivekey](doc/chantools_derivekey.md) | :pencil: (**CLN**) Derive a single private/public key from `lnd`'s seed, use to test seed | | [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 | | [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!) | | [dropgraphzombies](doc/chantools_dropgraphzombies.md) | Drop all zombie channels from a `channel.db` to force a graph re-sync | diff --git a/cmd/chantools/derivekey.go b/cmd/chantools/derivekey.go index 173c4fd..e9e69bd 100644 --- a/cmd/chantools/derivekey.go +++ b/cmd/chantools/derivekey.go @@ -1,10 +1,13 @@ package main import ( + "encoding/hex" + "errors" "fmt" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/hdkeychain" + "github.com/lightninglabs/chantools/cln" "github.com/lightninglabs/chantools/lnd" "github.com/spf13/cobra" ) @@ -27,6 +30,8 @@ type deriveKeyCommand struct { Neuter bool Identity bool + HsmSecret string + rootKey *rootKey cmd *cobra.Command } @@ -53,8 +58,14 @@ chantools derivekey --identity`, "only public key(s)", ) cc.cmd.Flags().BoolVar( - &cc.Identity, "identity", false, "derive the lnd "+ - "identity_pubkey", + &cc.Identity, "identity", false, "derive the node's identity "+ + "public key", + ) + 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, "decrypting the backup") @@ -63,6 +74,40 @@ chantools derivekey --identity`, } func (c *deriveKeyCommand) Execute(_ *cobra.Command, _ []string) error { + if c.HsmSecret != "" { + if c.Path != "" { + return errors.New("cannot specify --path with " + + "--hsm_secret, only identity key can be " + + "derived") + } + + secretBytes, err := hex.DecodeString(c.HsmSecret) + if err != nil { + return fmt.Errorf("error decoding HSM secret: %w", err) + } + + var hsmSecret [32]byte + copy(hsmSecret[:], secretBytes) + + nodePubKey, _, err := cln.NodeKey(hsmSecret) + if err != nil { + return fmt.Errorf("error deriving node key from HSM: "+ + "%w", err) + } + + result := fmt.Sprintf( + "Node identity public key: %x", + nodePubKey.SerializeCompressed(), + ) + fmt.Println(result) + + // For the tests, also log as trace level which is disabled by + // default. + log.Tracef(result) + + return nil + } + extendedKey, err := c.rootKey.read() if err != nil { return fmt.Errorf("error reading root key: %w", err) diff --git a/cmd/chantools/derivekey_test.go b/cmd/chantools/derivekey_test.go index 87a7768..deb19c7 100644 --- a/cmd/chantools/derivekey_test.go +++ b/cmd/chantools/derivekey_test.go @@ -99,8 +99,9 @@ func TestDeriveKeyXprv(t *testing.T) { err := derive.Execute(nil, nil) require.NoError(t, err) - h.assertLogContains("cQcdieZy2d1TAdCsa5MjmHJs2gdHcD7x22nDbhJyVTUa3Ax" + - "5KB3w") + h.assertLogContains( + "cQcdieZy2d1TAdCsa5MjmHJs2gdHcD7x22nDbhJyVTUa3Ax5KB3w", + ) } func TestDeriveKeyXpub(t *testing.T) { @@ -120,8 +121,10 @@ func TestDeriveKeyXpub(t *testing.T) { err := derive.Execute(nil, nil) require.NoError(t, err) - h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" + - "05eb1ab99aa05e01c2a") + h.assertLogContains( + "03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b04257105eb1ab99aa05e" + + "01c2a", + ) } func TestDeriveKeyXpubNoNeuter(t *testing.T) { @@ -140,6 +143,27 @@ func TestDeriveKeyXpubNoNeuter(t *testing.T) { err := derive.Execute(nil, nil) require.NoError(t, err) - h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" + - "05eb1ab99aa05e01c2a") + h.assertLogContains( + "03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b04257105eb1ab99aa05e" + + "01c2a", + ) +} + +func TestDeriveKeyHsmSecret(t *testing.T) { + h := newHarness(t) + + // Derive a specific key from the serialized root key. + derive := &deriveKeyCommand{ + Identity: true, + HsmSecret: "471a115fb8edd6281f883ac82509fb0f30707e590d895f683" + + "2d781f1639c07ff", + } + + err := derive.Execute(nil, nil) + require.NoError(t, err) + + h.assertLogContains( + "03508beb59d2ec4772cd7b143bbef0fdac204b240747e82bc5fe58bd0418" + + "4f35a3", + ) } diff --git a/doc/chantools_derivekey.md b/doc/chantools_derivekey.md index 41db9df..f9790d7 100644 --- a/doc/chantools_derivekey.md +++ b/doc/chantools_derivekey.md @@ -23,21 +23,23 @@ chantools derivekey --identity ### Options ``` - --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag - -h, --help help for derivekey - --identity derive the lnd identity_pubkey - --neuter don't output private key(s), only public key(s) - --path string BIP32 derivation path to derive; must start with "m/" - --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed - --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 + --bip39 read a classic BIP39 seed and passphrase from the terminal instead of asking for lnd seed format or providing the --rootkey flag + -h, --help help for derivekey + --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' + --identity derive the node's identity public key + --neuter don't output private key(s), only public key(s) + --path string BIP32 derivation path to derive; must start with "m/" + --rootkey string BIP32 HD root key of the wallet to use for decrypting the backup; leave empty to prompt for lnd 24 word aezeed + --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 ``` ### Options inherited from parent commands ``` - -r, --regtest Indicates if regtest parameters should be used - -s, --signet Indicates if the public signet parameters should be used - -t, --testnet Indicates if testnet parameters should be used + --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. + -r, --regtest Indicates if regtest parameters should be used + -s, --signet Indicates if the public signet parameters should be used + -t, --testnet Indicates if testnet parameters should be used ``` ### SEE ALSO