2
0
mirror of https://github.com/guggero/chantools synced 2024-11-11 01:10:42 +00:00

Add derivekey command

This commit is contained in:
Oliver Gugger 2020-01-04 13:11:11 +01:00
parent 506f761dd3
commit 759d9f60e0
No known key found for this signature in database
GPG Key ID: 8E4256593F177720
4 changed files with 121 additions and 0 deletions

View File

@ -51,6 +51,7 @@ Help Options:
-h, --help Show this help message -h, --help Show this help message
Available commands: Available commands:
derivekey Derive a key with a specific derivation path from the BIP32 HD root key.
dumpbackup Dump the content of a channel.backup file. dumpbackup Dump the content of a channel.backup file.
dumpchannels Dump all channel information from lnd's channel database. dumpchannels Dump all channel information from lnd's channel database.
forceclose Force-close the last state that is in the channel.db provided. forceclose Force-close the last state that is in the channel.db provided.
@ -62,6 +63,29 @@ Available commands:
## Commands ## Commands
### derivekey
```text
Usage:
chantools [OPTIONS] derivekey [derivekey-OPTIONS]
[derivekey command options]
--rootkey= BIP32 HD root key to derive the key from.
--path= The BIP32 derivation path to derive. Must start with "m/".
--neuter Do not output the private key, just the public key.
```
This command derives a single key with the given BIP32 derivation path from the
root key and prints it to the console. Make sure to escape apostrophes in the
derivation path.
Example command:
```bash
chantools derivekey --rootkey xprvxxxxxxxxxx --path m/1017\'/0\'/5\'/0/0 \
--neuter
```
### dumpbackup ### dumpbackup
```text ```text

42
cmd_derivekey.go Normal file
View File

@ -0,0 +1,42 @@
package chantools
import (
"fmt"
"github.com/btcsuite/btcutil"
"github.com/btcsuite/btcutil/hdkeychain"
)
func deriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
neuter bool) error {
fmt.Printf("Deriving path %s for network %s.\n", path, chainParams.Name)
parsedPath, err := parsePath(path)
if err != nil {
return fmt.Errorf("could not parse derivation path: %v", err)
}
derivedKey, err := deriveChildren(extendedKey, parsedPath)
if err != nil {
return fmt.Errorf("could not derive children: %v", err)
}
pubKey, err := derivedKey.ECPubKey()
if err != nil {
return fmt.Errorf("could not derive public key: %v", err)
}
fmt.Printf("Public key: %x\n", pubKey.SerializeCompressed())
if neuter {
return nil
}
privKey, err := derivedKey.ECPrivKey()
if err != nil {
return fmt.Errorf("could not derive private key: %v", err)
}
wif, err := btcutil.NewWIF(privKey, chainParams, true)
if err != nil {
return fmt.Errorf("could not encode WIF: %v", err)
}
fmt.Printf("Private key (WIF): %s\n", wif.String())
return nil
}

View File

@ -1,9 +1,12 @@
package chantools package chantools
import ( import (
"fmt"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcutil/hdkeychain" "github.com/btcsuite/btcutil/hdkeychain"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"strconv"
"strings"
) )
const ( const (
@ -26,6 +29,33 @@ func deriveChildren(key *hdkeychain.ExtendedKey, path []uint32) (
return currentKey, nil return currentKey, nil
} }
func parsePath(path string) ([]uint32, error) {
path = strings.TrimSpace(path)
if len(path) == 0 {
return nil, fmt.Errorf("path cannot be empty")
}
if !strings.HasPrefix(path, "m/") {
return nil, fmt.Errorf("path must start with m/")
}
parts := strings.Split(path, "/")
indices := make([]uint32, len(parts)-1)
for i := 1; i < len(parts); i++ {
index := uint32(0)
part := parts[i]
if strings.Contains(parts[i], "'") {
index += hardenedKeyStart
part = strings.TrimRight(parts[i], "'")
}
parsed, err := strconv.Atoi(part)
if err != nil {
return nil, fmt.Errorf("could not parse part \"%s\": "+
"%v", part, err)
}
indices[i-1] = index + uint32(parsed)
}
return indices, nil
}
type channelBackupEncryptionRing struct { type channelBackupEncryptionRing struct {
extendedKey *hdkeychain.ExtendedKey extendedKey *hdkeychain.ExtendedKey
chainParams *chaincfg.Params chainParams *chaincfg.Params

25
main.go
View File

@ -74,6 +74,10 @@ func Main() error {
"dumpbackup", "Dump the content of a channel.backup file.", "", "dumpbackup", "Dump the content of a channel.backup file.", "",
&dumpBackupCommand{}, &dumpBackupCommand{},
) )
_, _ = parser.AddCommand(
"derivekey", "Derive a key with a specific derivation path "+
"from the BIP32 HD root key.", "", &deriveKeyCommand{},
)
_, err := parser.Parse() _, err := parser.Parse()
return err return err
@ -293,6 +297,27 @@ func (c *dumpBackupCommand) Execute(_ []string) error {
return dumpChannelBackup(multi) return dumpChannelBackup(multi)
} }
type deriveKeyCommand struct {
RootKey string `long:"rootkey" description:"BIP32 HD root key to derive the key from."`
Path string `long:"path" description:"The BIP32 derivation path to derive. Must start with \"m/\"."`
Neuter bool `long:"neuter" description:"Do not output the private key, just the public key."`
}
func (c *deriveKeyCommand) Execute(_ []string) error {
setupChainParams(cfg)
// Check that root key is valid.
if c.RootKey == "" {
return fmt.Errorf("root key is required")
}
extendedKey, err := hdkeychain.NewKeyFromString(c.RootKey)
if err != nil {
return fmt.Errorf("error parsing root key: %v", err)
}
return deriveKey(extendedKey, c.Path, c.Neuter)
}
func setupChainParams(cfg *config) { func setupChainParams(cfg *config) {
if cfg.Testnet { if cfg.Testnet {
chainParams = &chaincfg.TestNet3Params chainParams = &chaincfg.TestNet3Params