2020-01-04 22:31:38 +00:00
|
|
|
package main
|
2020-01-04 12:11:11 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-10-29 12:43:10 +00:00
|
|
|
|
2022-07-18 06:52:40 +00:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
|
|
"github.com/btcsuite/btcd/btcutil/hdkeychain"
|
2023-05-31 09:12:42 +00:00
|
|
|
"github.com/lightninglabs/chantools/lnd"
|
2020-12-27 00:09:34 +00:00
|
|
|
"github.com/spf13/cobra"
|
2020-01-04 12:11:11 +00:00
|
|
|
)
|
|
|
|
|
2021-01-01 11:56:19 +00:00
|
|
|
const deriveKeyFormat = `
|
|
|
|
Path: %s
|
|
|
|
Network: %s
|
|
|
|
Public key: %x
|
|
|
|
Extended public key (xpub): %v
|
|
|
|
Address: %v
|
|
|
|
Legacy address: %v
|
2022-10-29 12:30:37 +00:00
|
|
|
Taproot address: %v
|
2021-01-01 11:56:19 +00:00
|
|
|
Private key (WIF): %s
|
|
|
|
Extended private key (xprv): %s
|
|
|
|
`
|
|
|
|
|
2020-01-04 22:31:38 +00:00
|
|
|
type deriveKeyCommand struct {
|
2021-05-02 14:45:36 +00:00
|
|
|
Path string
|
|
|
|
Neuter bool
|
|
|
|
Identity bool
|
2020-01-04 22:31:38 +00:00
|
|
|
|
2020-12-27 00:09:34 +00:00
|
|
|
rootKey *rootKey
|
2020-12-27 00:40:51 +00:00
|
|
|
cmd *cobra.Command
|
2020-12-27 00:09:34 +00:00
|
|
|
}
|
2020-01-04 22:31:38 +00:00
|
|
|
|
2020-12-27 00:09:34 +00:00
|
|
|
func newDeriveKeyCommand() *cobra.Command {
|
|
|
|
cc := &deriveKeyCommand{}
|
|
|
|
cc.cmd = &cobra.Command{
|
2020-12-27 00:40:51 +00:00
|
|
|
Use: "derivekey",
|
2020-12-27 00:09:34 +00:00
|
|
|
Short: "Derive a key with a specific derivation path",
|
2020-12-27 00:40:51 +00:00
|
|
|
Long: `This command derives a single key with the given BIP32
|
|
|
|
derivation path from the root key and prints it to the console.`,
|
2021-05-02 15:40:13 +00:00
|
|
|
Example: `chantools derivekey --path "m/1017'/0'/5'/0/0'" \
|
2021-05-02 14:45:36 +00:00
|
|
|
--neuter
|
|
|
|
|
|
|
|
chantools derivekey --identity`,
|
2020-12-27 00:09:34 +00:00
|
|
|
RunE: cc.Execute,
|
|
|
|
}
|
|
|
|
cc.cmd.Flags().StringVar(
|
|
|
|
&cc.Path, "path", "", "BIP32 derivation path to derive; must "+
|
|
|
|
"start with \"m/\"",
|
|
|
|
)
|
|
|
|
cc.cmd.Flags().BoolVar(
|
|
|
|
&cc.Neuter, "neuter", false, "don't output private key(s), "+
|
|
|
|
"only public key(s)",
|
2020-01-04 22:48:35 +00:00
|
|
|
)
|
2021-05-02 14:45:36 +00:00
|
|
|
cc.cmd.Flags().BoolVar(
|
|
|
|
&cc.Identity, "identity", false, "derive the lnd "+
|
|
|
|
"identity_pubkey",
|
|
|
|
)
|
2020-01-04 22:58:10 +00:00
|
|
|
|
2020-12-27 00:09:34 +00:00
|
|
|
cc.rootKey = newRootKey(cc.cmd, "decrypting the backup")
|
2020-12-27 00:40:51 +00:00
|
|
|
|
2020-12-27 00:09:34 +00:00
|
|
|
return cc.cmd
|
|
|
|
}
|
2020-01-04 22:48:35 +00:00
|
|
|
|
2020-12-27 00:09:34 +00:00
|
|
|
func (c *deriveKeyCommand) Execute(_ *cobra.Command, _ []string) error {
|
|
|
|
extendedKey, err := c.rootKey.read()
|
2020-01-04 22:31:38 +00:00
|
|
|
if err != nil {
|
2022-07-18 07:50:27 +00:00
|
|
|
return fmt.Errorf("error reading root key: %w", err)
|
2020-01-04 22:31:38 +00:00
|
|
|
}
|
|
|
|
|
2021-05-02 14:45:36 +00:00
|
|
|
if c.Identity {
|
|
|
|
c.Path = lnd.IdentityPath(chainParams)
|
|
|
|
c.Neuter = true
|
|
|
|
}
|
|
|
|
|
2020-01-04 22:31:38 +00:00
|
|
|
return deriveKey(extendedKey, c.Path, c.Neuter)
|
|
|
|
}
|
|
|
|
|
2020-01-04 12:11:11 +00:00
|
|
|
func deriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
|
|
|
|
neuter bool) error {
|
|
|
|
|
2020-04-26 20:36:44 +00:00
|
|
|
child, pubKey, wif, err := lnd.DeriveKey(extendedKey, path, chainParams)
|
2020-01-04 12:11:11 +00:00
|
|
|
if err != nil {
|
2022-07-18 07:50:27 +00:00
|
|
|
return fmt.Errorf("could not derive keys: %w", err)
|
2020-01-04 12:11:11 +00:00
|
|
|
}
|
2020-04-26 20:36:44 +00:00
|
|
|
neutered, err := child.Neuter()
|
|
|
|
if err != nil {
|
2022-07-18 07:50:27 +00:00
|
|
|
return fmt.Errorf("could not neuter child key: %w", err)
|
2020-04-26 20:36:44 +00:00
|
|
|
}
|
2020-01-04 12:11:11 +00:00
|
|
|
|
2020-06-14 10:51:35 +00:00
|
|
|
// Print the address too.
|
|
|
|
hash160 := btcutil.Hash160(pubKey.SerializeCompressed())
|
|
|
|
addrP2PKH, err := btcutil.NewAddressPubKeyHash(hash160, chainParams)
|
|
|
|
if err != nil {
|
2022-07-18 07:50:27 +00:00
|
|
|
return fmt.Errorf("could not create address: %w", err)
|
2020-06-14 10:51:35 +00:00
|
|
|
}
|
|
|
|
addrP2WKH, err := btcutil.NewAddressWitnessPubKeyHash(
|
|
|
|
hash160, chainParams,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2022-07-18 07:50:27 +00:00
|
|
|
return fmt.Errorf("could not create address: %w", err)
|
2020-06-14 10:51:35 +00:00
|
|
|
}
|
|
|
|
|
2022-10-29 12:30:37 +00:00
|
|
|
addrP2TR, err := lnd.P2TRAddr(pubKey, chainParams)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not create address: %w", err)
|
|
|
|
}
|
|
|
|
|
2021-01-01 12:28:27 +00:00
|
|
|
privKey, xPriv := na, na
|
2020-03-30 16:33:00 +00:00
|
|
|
if !neuter {
|
2021-01-01 11:56:19 +00:00
|
|
|
privKey, xPriv = wif.String(), child.String()
|
2020-01-04 12:11:11 +00:00
|
|
|
}
|
|
|
|
|
2021-01-01 11:56:19 +00:00
|
|
|
result := fmt.Sprintf(
|
|
|
|
deriveKeyFormat, path, chainParams.Name,
|
|
|
|
pubKey.SerializeCompressed(), neutered, addrP2WKH, addrP2PKH,
|
2022-10-29 12:30:37 +00:00
|
|
|
addrP2TR, privKey, xPriv,
|
2021-01-01 11:56:19 +00:00
|
|
|
)
|
2021-01-01 12:28:27 +00:00
|
|
|
fmt.Println(result)
|
2021-01-01 11:56:19 +00:00
|
|
|
|
|
|
|
// For the tests, also log as trace level which is disabled by default.
|
|
|
|
log.Tracef(result)
|
|
|
|
|
2020-01-04 12:11:11 +00:00
|
|
|
return nil
|
|
|
|
}
|