mirror of
https://github.com/guggero/chantools
synced 2024-11-11 01:10:42 +00:00
Add derivekey command
This commit is contained in:
parent
506f761dd3
commit
759d9f60e0
24
README.md
24
README.md
@ -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
42
cmd_derivekey.go
Normal 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
|
||||||
|
}
|
@ -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
25
main.go
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user